'যদি' একটি শট লেখার সর্বাধিক মার্জিত উপায়


136

যেহেতু সি ++ ১ one জন একটি ifব্লক লিখতে পারে যা ঠিক এর মতো একবার কার্যকর করা হবে:

#include <iostream>
int main() {
    for (unsigned i = 0; i < 10; ++i) {

        if (static bool do_once = true; do_once) { // Enter only once
            std::cout << "hello one-shot" << std::endl;
            // Possibly much more code
            do_once = false;
        }

    }
}

আমি জানি যে আমি এটির ওভারটিনিং করছিলাম, এবং এটি সমাধানের অন্যান্য উপায় রয়েছে, তবে এখনও - এটি কি এইভাবে কোনওভাবে লেখা সম্ভব, তাই do_once = falseশেষের কোনও প্রয়োজন নেই ?

if (DO_ONCE) {
    // Do stuff
}

আমি একটি সহায়ক ফাংশন ভাবছি do_once(), এতে থাকা static bool do_once, তবে আমি যদি একই জায়গায় বিভিন্ন জায়গায় ব্যবহার করতে চাই তবে কী হবে? এটি একটি সময় এবং স্থান হতে পারে #define? আমি আশা করি না.


52
শুধু কেন নয় if (i == 0)? এটা যথেষ্ট পরিষ্কার।
সিলভানোসার্জা

26
@ সিলভানো চেরজা কারণ এটি মুখ্য নয়। এই ইফ-ব্লকটি এমন কোনও স্থানে হতে পারে যা নিয়মিত লুপে নয়, একাধিকবার কার্যকর করা হয়
nada

8
হতে পারে std::call_onceএকটি বিকল্প (এটি থ্রেডিংয়ের জন্য ব্যবহৃত হয় তবে এটি এখনও কাজ করে)।
ফাদান

25
আপনার উদাহরণটি আপনার রিয়েল-ওয়ার্ল্ড সমস্যার খারাপ প্রতিচ্ছবি হতে পারে যা আপনি আমাদের দেখান না, তবে কেন কেবল কল-একবারের ফাংশনটি লুপের বাইরে তুলবেন না?
রুবনেভবি

14
এটা আমার কাছে ঘটেনি একটি ইন সক্রিয়া ভেরিয়েবল যে ifঅবস্থার হতে পারে static। যে চালাক।
হলিব্ল্যাকগেট

উত্তর:


143

ব্যবহার std::exchange:

if (static bool do_once = true; std::exchange(do_once, false))

আপনি সত্যের বিপরীতে এটিকে আরও খাটো করে তুলতে পারেন:

if (static bool do_once; !std::exchange(do_once, true))

তবে আপনি যদি এটি ব্যবহার করে থাকেন তবে অভিনব হবেন না এবং তার পরিবর্তে একটি মোড়ক তৈরি করুন:

struct Once {
    bool b = true;
    explicit operator bool() { return std::exchange(b, false); }
};

এবং এটি ব্যবহার করুন:

if (static Once once; once)

ভেরিয়েবলটি শর্তের বাইরে উল্লেখ করার কথা নয়, তাই নামটি আমাদের বেশি কিনে না। পাইথনের মতো অন্যান্য ভাষা থেকে অনুপ্রেরণা গ্রহণ করে যা _সনাক্তকারীকে একটি বিশেষ অর্থ দেয় , আমরা লিখতে পারি:

if (static Once _; _)

আরও উন্নতি: বিএসএস বিভাগ (@ ডডুকিপ্লেটার) এর সুবিধা নিন, আমরা ইতিমধ্যে (@ শ্যাডোএ্যাঞ্জার) চালানোর পরে মেমরি রচনাটি এড়িয়ে চলুন এবং আপনি যদি অনেকবার পরীক্ষা করতে যাচ্ছেন তবে (যেমন প্রশ্নের মতো) একটি শাখার পূর্বাভাসের ইঙ্গিত দিন:

// GCC, Clang, icc only; use [[likely]] in C++20 instead
#define likely(x) __builtin_expect(!!(x), 1)

struct Once {
    bool b = false;
    explicit operator bool()
    {
        if (likely(b))
            return false;

        b = true;
        return true;
    }
};

32
আমি জানি ম্যাক্রো সি ++ তে প্রচুর ঘৃণা পেয়েছে তবে এটি কেবল এতটাই পরিষ্কার দেখায়: #define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))হিসাবে ব্যবহার করা যায়:ONLY_ONCE { foo(); }
ফিবলস

5
মানে, যদি আপনি তিনবার "একবার" লিখেন তবে এটি বিবৃতিতে যদি এটি মূল্যবান হয় তবে এটি তিনবারের বেশি ব্যবহার করুন
অ্যালান

13
_অনুবাদযোগ্য স্ট্রিংগুলি চিহ্নিত করতে নামটি প্রচুর সফ্টওয়্যারে ব্যবহৃত হয়। আকর্ষণীয় জিনিস ঘটবে আশা করি।
সাইমন রিখটার

1
আপনার যদি পছন্দ থাকে তবে অল-বিট-শূন্য হতে স্থির অবস্থার প্রাথমিক মানটি পছন্দ করুন। সর্বাধিক এক্সিকিউটেবল-ফর্ম্যাটগুলিতে একটি সমস্ত-শূন্য অঞ্চলের দৈর্ঘ্য থাকে।
প্রতিলিপি

7
_ভেরিয়েবলের জন্য ব্যবহার করা হবে অ-পাইথোনিক। আপনি ব্যবহার করবেন না _ভেরিয়েবল যা পরে শুধুমাত্র দোকান মান উল্লেখ করা হবে, যেখানে তোমার জন্য আছে একটি পরিবর্তনশীল প্রদান কিন্তু আপনি মান প্রয়োজন হবে না। এটি যখন আনপ্যাকিংয়ের জন্য সাধারণত ব্যবহৃত হয় যখন আপনার কেবলমাত্র কয়েকটি মান প্রয়োজন হয়। (অন্যান্য ব্যবহারের ক্ষেত্রেও রয়েছে, তবে তারা
নিক্ষিপ্ত

91

সম্ভবত সবচেয়ে মার্জিত সমাধান নয় এবং আপনি কোনও বাস্তব দেখতে পাচ্ছেন না if, তবে মানক গ্রন্থাগারটি আসলে এই কেসটি কভার করে :, দেখুন std::call_once

#include <mutex>

std::once_flag flag;

for (int i = 0; i < 10; ++i)
    std::call_once(flag, [](){ std::puts("once\n"); });

এখানে সুবিধা হ'ল এটি থ্রেড নিরাপদ।


3
আমি এই প্রসঙ্গে std :: কল_অনসেস সম্পর্কে সচেতন ছিলাম না। তবে এই সমাধানের সাহায্যে আপনি যে স্টাড :: কল_অনস ব্যবহার করবেন তার প্রতিটি জায়গার জন্য একবার_ফ্লেগ ঘোষণা করতে হবে, তাই না?
নাদা

11
এটি কাজ করে, তবে সমাধানের কোনও সহজ সমাধানের জন্য নয়, ধারণাটি মাল্টিথ্রেডেড অ্যাপ্লিকেশনগুলির জন্য। এটি এমন কোনও সহজ কিছুর জন্য ওভারকিল কারণ এটি অভ্যন্তরীণ সিঙ্ক্রোনাইজেশন ব্যবহার করে - এমন কোনও কিছুর জন্য যা যদি কোনও সমতল দ্বারা সমাধান করা হয় তবে। তিনি কোনও থ্রেড নিরাপদ সমাধান চাইছেন না।
মাইকেল চৌর্দাকিস

9
@ মিশেলচৌড়ডাকিস আমি আপনার সাথে একমত, এটি একটি অতিমাত্রার কাজ। তবে এটি সম্পর্কে জেনে রাখা মূল্যবান এবং বিশেষত আপনি যদি কিছুটা পঠনযোগ্য তবে ট্র্যাফিকের পিছনে কিছু লুকিয়ে রাখার পরিবর্তে ("এই কোডটি একবার কার্যকর করুন") প্রকাশ করার সম্ভাবনা সম্পর্কে জেনে রাখা।
lubgr

17
ওহ, call_onceআমাকে দেখার অর্থ আপনি একবার কিছু কল করতে চান। পাগল, আমি জানি।
ব্যারি

3
@ সের্গেইএ কারণ এটি অভ্যন্তরীণ সিঙ্ক্রোনাইজেশন ব্যবহার করে - এমন কোনও কিছুর জন্য যা যদি একটি সমতল দ্বারা সমাধান করা হয়। যা চাওয়া হয়েছিল তা বাদ দিয়ে অন্য কিছু করার এটি একটি বোকামিযুক্ত উপায়।
অরবিট

52

সি ++ এর মধ্যে একটি বিল্টিন নিয়ন্ত্রণ প্রবাহ আদিম রয়েছে যা ইতিমধ্যে "( ব্লক- পূর্ববর্তী ; শর্ত; পরে-ব্লক )" নিয়ে গঠিত :

for (static bool b = true; b; b = false)

বা হ্যাকিয়র তবে খাটো:

for (static bool b; !b; b = !b)

তবে আমি মনে করি যে এখানে যে কোনও কৌশল উপস্থাপন করা হয়েছে সেগুলি যত্ন সহকারে ব্যবহার করা উচিত, কারণ সেগুলি (এখনও?) খুব সাধারণ নয়।


1
আমি প্রথম বিকল্পটি পছন্দ করি (যদিও - এখানে অনেকগুলি বৈকল্পের মতো - এটি থ্রেড নিরাপদ নয়, তাই সাবধানতা অবলম্বন করুন)। দ্বিতীয় বিকল্প আমাকে (কঠিন শরীর ঠান্ডা হয়ে যাওয়া পড়তে এবং শুধুমাত্র দুটি থ্রেড সঙ্গে সময়ের কোন সংখ্যা মৃত্যুদন্ড কার্যকর করা যেতে পারে দেয় ... b == false: Thread 1মূল্যায়ণ !bএবং লুপ জন্য প্রবেশ Thread 2মূল্যায়ণ !bএবং লুপ জন্য প্রবেশ করে, Thread 1সেটিং, লুপ তার কাপড় এবং পাতা করেন b == falseকরার !bঅর্থাত b = true... Thread 2তার কাপড় এবং পাতা লুপ জন্য, সেটিং করে b == trueকরা !bঅর্থাত b = falseপুরো প্রক্রিয়া যার ফলে অনির্দিষ্টকালের জন্য পুনরাবৃত্তি করা)
CharonX

3
আমি এটাকে ব্যঙ্গাত্মক মনে করি যে কোনও সমস্যার সর্বাধিক মার্জিত সমাধানগুলির মধ্যে একটি, যেখানে কিছু কোড কেবল একবার কার্যকর করা হয় বলে মনে করা হয় এটি একটি লুপ । +1
নাদা

2
আমি এড়াতে চাই b=!b, এটি দেখতে সুন্দর লাগছে, তবে আপনি আসলে মানটি মিথ্যা হতে চান, তাই b=falseপছন্দ করা উচিত।
yo '

2
সচেতন থাকুন যে সুরক্ষিত ব্লকটি স্থানীয়ভাবে বহির্মুখী থেকে বেরিয়ে এলে তা আবার চলবে। এটি এমনকি কাম্য হতে পারে, তবে এটি অন্যান্য সমস্ত পদ্ধতির থেকে পৃথক।
ডেভিস হ্যারিং

29

সি ++ 17 এ আপনি লিখতে পারেন

if (static int i; i == 0 && (i = 1)){

iলুপ শরীরের সাথে চারপাশে খেলা এড়াতে যাতে । i0 দিয়ে শুরু হয় (আদর্শ দ্বারা নিশ্চিত), এবং পরে অভিব্যক্তি ;সেট iকরার 1প্রথমবার এটা মূল্যায়ন করা হয়।

মনে রাখবেন যে সি ++ 11 এ আপনি ল্যাম্বডা ফাংশন দিয়ে একই অর্জন করতে পারেন

if ([]{static int i; return i == 0 && (i = 1);}()){

এটিতে সামান্য সুবিধাও রয়েছে যা iলুপের শরীরে ফাঁস হয় না।


4
আমি দুঃখের সাথে বলতে চাই - যদি সেটিকে CALL_ONCE নামক একটি # নির্ধারিত উপায়ে রাখা হত বা এটি আরও পাঠযোগ্য হয়
nada

9
যদিও static int i;সম্ভবত (আমি সত্যই নিশ্চিত নই) সেগুলির মধ্যে একটি হতে পারে যেখানে iপ্রাথমিক পর্যায়ে যাওয়ার গ্যারান্টিযুক্ত রয়েছে 0, এটি static int i = 0;এখানে ব্যবহার করা আরও পরিষ্কার ।
কাইল উইলমন

7
নির্বিশেষে আমি সম্মতি দিচ্ছি যে একটি প্রাথমিককরণকারী অনুধাবনের জন্য একটি ভাল ধারণা
অরবিট

5
@ বাথশেবা কাইল করেন নি, সুতরাং আপনার দাবি ইতিমধ্যে মিথ্যা প্রমাণিত হয়েছে। পরিষ্কার কোডের জন্য দুটি অক্ষর যুক্ত করতে আপনার কী ব্যয় হয়? আসুন, আপনি একজন "প্রধান সফটওয়্যার আর্কিটেক্ট"; আপনার এটি জানা উচিত :)
অরবিট

5
আপনি যদি ভেরিয়েবলের প্রাথমিক মানটি বানান পরিষ্কারের বিপরীত হয় বা পরামর্শ দেন যে "মজাদার কিছু ঘটছে", আমি মনে করি না যে আপনাকে সাহায্য করা যেতে পারে;)
অরবিট

14
static bool once = [] {
  std::cout << "Hello one-shot\n";
  return false;
}();

এই সমাধানটি থ্রেড নিরাপদ (অন্যান্য অনেক পরামর্শের মতো নয়)।


3
আপনি কি জানেন ()যে ল্যাম্বডা ঘোষণায় alচ্ছিক (খালি থাকলে)?
ননিমে

9

আপনি শর্তাধীনের স্থলে ইনস্ট্যান্ট করে স্থিতিক বস্তুর নির্মাত্রে এককালীন ক্রিয়াটি মোড়াতে পারেন।

উদাহরণ:

#include <iostream>
#include <functional>

struct do_once {
    do_once(std::function<void(void)> fun) {
        fun();
    }
};

int main()
{
    for (int i = 0; i < 3; ++i) {
        static do_once action([](){ std::cout << "once\n"; });
        std::cout << "Hello World\n";
    }
}

অথবা আপনি প্রকৃতপক্ষে কোনও ম্যাক্রো দিয়ে আটকে থাকতে পারেন যা দেখতে এরকম কিছু দেখতে পারে:

#include <iostream>

#define DO_ONCE(exp) \
do { \
  static bool used_before = false; \
  if (used_before) break; \
  used_before = true; \
  { exp; } \
} while(0)  

int main()
{
    for (int i = 0; i < 3; ++i) {
        DO_ONCE(std::cout << "once\n");
        std::cout << "Hello World\n";
    }
}

8

@ ডিমন বলেছিলেন, আপনি হ্রাসকারী std::exchangeপূর্ণসংখ্যার ব্যবহার করে ব্যবহার এড়াতে পারবেন , তবে আপনাকে মনে রাখতে হবে যে নেতিবাচক মানগুলি সত্যের দিকে আসে। এটি ব্যবহারের উপায় হ'ল:

if (static int n_times = 3; n_times && n_times--)
{
    std::cout << "Hello world x3" << std::endl;
} 

@ অ্যাকর্নের অভিনব র‍্যাপারে এটি অনুবাদ করা দেখতে এরকম দেখাবে:

struct n_times {
    int n;
    n_times(int number) {
        n = number;
    };
    explicit operator bool() {
        return n && n--;
    };
};

...

if(static n_times _(2); _)
{
    std::cout << "Hello world twice" << std::endl;
}

7

std::exchange@ অ্যাকর্নের পরামর্শ অনুসারে ব্যবহার করা সম্ভবত সর্বাধিক প্রতিযোগী উপায়, একটি এক্সচেঞ্জ অপারেশন অগত্যা সস্তা নয়। যদিও অবশ্যই স্থির সূচনাটি থ্রেড-সেফ হওয়ার গ্যারান্টিযুক্ত (যদি না আপনি আপনার সংকলকটি এটি না করতে বলেন) তবে staticকীওয়ার্ডের উপস্থিতিতে পারফরম্যান্স সম্পর্কে কোনও বিবেচনা যাইহোক কিছুটা নিরর্থক ।

আপনি (সি ++ ব্যবহার প্রায়ই মানুষ হিসেবে) মাইক্রো-অপ্টিমাইজেশান নিয়ে উদ্বিগ্ন হয়, আপনি ভাল হিসাবে স্ক্র্যাচ পারে boolএবং ব্যবহার intপরিবর্তে, যা আপনি পোস্ট হ্রাস (বরং বা, ব্যবহার করার অনুমতি দেবে বৃদ্ধি , অসদৃশ যেমন booldecrementing একটি intহবে না শুন্যতে সুসিক্ত ...):

if(static int do_once = 0; !do_once++)

boolএটিতে যেগুলি ইনক্রিমেন্ট / হ্রাস অপারেটরগুলি ছিল, কিন্তু সেগুলি অনেক আগেই অবহেলা করা হয়েছিল (সি ++ 11? নিশ্চিত নয়?) এবং সি ++ 17 এ পুরোপুরি সরানো হবে। তবুও আপনি একটি intজরিমানা হ্রাস করতে পারেন , এবং এটি অবশ্যই বুলিয়ান শর্ত হিসাবে কাজ করবে।

বোনাস: আপনি বাস্তবায়ন করতে পারেন do_twiceবা do_thriceএকইভাবে ...


আমি এটি পরীক্ষা করেছি এবং এটি প্রথমবার ব্যতীত একাধিকবার অগ্নিসংযোগ করেছে।
নাদা

@ নাদা: বোকা আমাকে, ঠিক বলেছেন ... ঠিক করে দিয়েছেন। এটি boolএকবারে সাথে কাজ করত এবং হ্রাস পায়। কিন্তু ইনক্রিমেন্ট ভাল কাজ করে int। অনলাইন ডেমো দেখুন: coliru.stacked-crooked.com/a/ee83f677a9c0c85a
দামন

1
এটির এখনও সমস্যা রয়েছে যে এটি do_onceপ্রায়শই গুটিয়ে যায় এবং শেষ পর্যন্ত আবার 0 টি (এবং আবার এবং আবার ...) মারবে।
নাদা

আরও সুনির্দিষ্টভাবে বলতে গেলে: এটি এখন প্রতি INT_MAX বার কার্যকর করা হবে।
nada

আচ্ছা হ্যাঁ, তবে লুপ কাউন্টারটি ছাড়াও সেই ক্ষেত্রে মোড়ানো, এটি খুব কমই সমস্যা। কিছু লোক মোটেই 2 বিলিয়ন (বা স্বাক্ষরিত না হলে 4 বিলিয়ন) চালায়। যদি তারা তা করে তবে তারা 64৪-বিট পূর্ণসংখ্যা ব্যবহার করতে পারে। দ্রুততম উপলব্ধ কম্পিউটারটি ব্যবহার করে, এটি প্রায় আবৃত হওয়ার আগেই আপনি মারা যাবেন, সুতরাং আপনার পক্ষে মামলা করা যাবে না।
দামন

4

এর জন্য @ বাথশেবার দুর্দান্ত উত্তরের ভিত্তিতে - এটিকে আরও সহজ করে তুলেছে।

ইন C++ 17, আপনি সহজভাবে করতে পারেন:

if (static int i; !i++) {
  cout << "Execute once";
}

(পূর্ববর্তী সংস্করণগুলিতে, কেবলমাত্র int iব্লকের বাইরে ঘোষণা করুন C সি :) তেও কাজ করে)।

সাধারণ কথায়: আপনি i ঘোষণা করুন, যা শূন্যের ডিফল্ট মান নেয় ( 0)। জিরো মিথ্যা, সুতরাং আমরা !এটিকে অস্বীকার করার জন্য উদ্দীপনা চিহ্ন ( ) অপারেটরটি ব্যবহার করি। তারপরে আমরা <ID>++অপারেটরের ইনক্রিমেন্ট সম্পত্তি বিবেচনা করি যা প্রথমে প্রক্রিয়াজাত হয় (নির্ধারিত, ইত্যাদি) এবং তারপরে বর্ধিত হয়।

অতএব, এই ব্লকে, আমি আরম্ভ করা হবে এবং মানটি 0একবারে হবে, যখন ব্লকটি কার্যকর হবে এবং তারপরে মানটি বৃদ্ধি পাবে। আমরা !এটিকে উপেক্ষা করার জন্য কেবল অপারেটর ব্যবহার করি।


1
এটি যদি আগে পোস্ট করা হত তবে এটি সম্ভবত এখনই গৃহীত উত্তর হবে। অান্তরিক ধন্যবাদ!
নাদা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.