खोज…
परिचय
लूप स्टेटमेंट तब तक बार-बार स्टेटमेंट्स को निष्पादित करता है जब तक कि कोई शर्त पूरी नहीं हो जाती। C ++ में 3 प्रकार के आदिम लूप हैं: के लिए, जबकि, और ... करते समय।
वाक्य - विन्यास
- जबकि ( स्थिति ) कथन ;
- जबकि कथन ( अभिव्यक्ति );
- for ( for-init-statement ; कंडीशन ; एक्सप्रेशन ) स्टेटमेंट ;
- for ( for-range-घोषणापत्र : for-range-initializer ) कथन ;
- टूटना ;
- जारी रखें ;
टिप्पणियों
algorithm
कॉल आमतौर पर हाथ से लिखे गए लूप के लिए बेहतर होते हैं।
यदि आप कुछ चाहते हैं जो एक एल्गोरिथ्म पहले से ही करता है (या कुछ समान है), एल्गोरिथ्म कॉल स्पष्ट है, अक्सर अधिक कुशल और कम त्रुटि प्रवण होता है।
यदि आपको एक लूप की आवश्यकता होती है जो कुछ सरल करता है (लेकिन यदि आप एक एल्गोरिथ्म का उपयोग कर रहे थे तो बाइंडर्स और एडेप्टर की एक उलझन वाली उलझन की आवश्यकता होगी), तो बस लूप लिखें।
रेंज-बेस्ड फॉर
एक सांख्यिक सूचकांक का उपयोग किए बिना या पुनरावृत्तियों को सीधे एक्सेस करने के for
लूप्स का उपयोग इट्रेटर-आधारित श्रेणी के तत्वों पर पुनरावृति करने के लिए किया जा सकता है:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
इस ओवर में हर तत्व पुनरावृति जाएगा v
के साथ, val
वर्तमान तत्व का मान रही है। निम्नलिखित कथन:
for (for-range-declaration : for-range-initializer ) statement
के बराबर है:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
यह परिवर्तन C ++ 20 में रेंज टीएस के नियोजित समर्थन के लिए पेश किया गया था।
इस मामले में, हमारा लूप इसके बराबर है:
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
ध्यान दें कि auto val
एक मूल्य प्रकार की घोषणा करता है, जो रेंज में संग्रहीत मूल्य की एक प्रति होगी (हम इसे चलते समय इटरेटर से इसकी प्रतिलिपि बना रहे हैं)। यदि श्रेणी में संग्रहीत मान कॉपी करने के लिए महंगे हैं, तो आप const auto &val
का उपयोग करना चाह सकते हैं। आपको auto
का उपयोग करने की भी आवश्यकता नहीं है; आप एक उपयुक्त टाइपनेम का उपयोग कर सकते हैं, जब तक कि यह रेंज के मूल्य प्रकार से स्पष्ट रूप से परिवर्तनीय हो।
यदि आपको पुनरावृत्त तक पहुँच की आवश्यकता है, तो रेंज-आधारित आपकी मदद नहीं कर सकता (कुछ प्रयास के बिना नहीं, कम से कम)।
यदि आप इसे संदर्भित करना चाहते हैं, तो आप ऐसा कर सकते हैं:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
आप पर पुनरावृति सकता const
यदि आपके पास संदर्भ const
कंटेनर:
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
जब अनुक्रम पुनरावृत्ति एक छद्म वस्तु लौटाता है और आपको उस वस्तु को गैर- const
तरीके से संचालित करने की आवश्यकता होती है, तो अग्रेषण संदर्भों का उपयोग करेगा। नोट: यह आपके कोड के पाठकों को सबसे अधिक भ्रमित करेगा।
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
"श्रेणी" के लिए रेंज-आधारित के for
प्रदान किया गया प्रकार निम्न में से एक हो सकता है:
भाषा सरणियाँ:
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; }
ध्यान दें कि डायनामिक ऐरे को आवंटित करने की गिनती नहीं है:
float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
किसी भी प्रकार के सदस्य कार्य
begin()
औरend()
, जो टाइप के तत्वों में पुनरावृत्तियों को लौटाते हैं। मानक पुस्तकालय कंटेनर योग्य होते हैं, लेकिन उपयोगकर्ता-परिभाषित प्रकारों का उपयोग किया जा सकता है:struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
कोई भी प्रकार जिसमें गैर-सदस्य
begin(type)
औरend(type)
फ़ंक्शन जोtype
आधार पर तर्क निर्भर लुकअप के माध्यम से पाया जा सकता है। यह श्रेणी प्रकार को संशोधित किए बिना एक श्रेणी प्रकार बनाने के लिए उपयोगी है:namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
पाश के लिए
एक for
पाश में बयान निष्पादित करता है loop body
है, जबकि पाश condition
सच है। लूप initialization statement
से पहले ठीक एक बार निष्पादित किया जाता है। प्रत्येक चक्र के बाद, iteration execution
भाग निष्पादित किया जाता है।
लूप के for
एक परिभाषित किया गया है:
for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
// body of the loop
}
प्लेसहोल्डर के बयानों की व्याख्या:
-
initialization statement
: इस बयान की शुरुआत में केवल एक बार निष्पादित हो जाता है,for
पाश। आप एक प्रकार के कई चरों की घोषणा कर सकते हैं, जैसे किint i = 0, a = 2, b = 3
। ये चर केवल लूप के दायरे में मान्य हैं। लूप के निष्पादन के दौरान एक ही नाम के साथ लूप से पहले परिभाषित चर को छिपा दिया जाता है। -
condition
: यह कथन प्रत्येक लूप बॉडी निष्पादन के आगे मूल्यांकन किया जाता है, और यदि यहfalse
मूल्यांकन करता है तो लूप को रोक देता है। -
iteration execution
: यह बयान, पाश शरीर के बाद मार डाला जाता है, अगले हालत मूल्यांकन के आगे जब तकfor
पाश शरीर में निरस्त किया गया है (द्वाराbreak
,goto
,return
या एक अपवाद फेंका जा रहा है)। आपiteration execution
भाग में कई कथन दर्ज कर सकते हैं, जैसे किa++, b+=10, c=b+a
।
एक के किसी न किसी बराबर for
पाश, एक के रूप में लिखा while
पाश है:
/*initialization*/
while (/*condition*/)
{
// body of the loop; using 'continue' will skip to increment part below
/*iteration execution*/
}
एक का उपयोग कर सबसे सामान्य रूप में for
पाश बयान बार की एक विशिष्ट संख्या निष्पादित करने के लिए है। उदाहरण के लिए, निम्नलिखित पर विचार करें:
for(int i = 0; i < 10; i++) {
std::cout << i << std::endl;
}
एक मान्य लूप भी है:
for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
std::cout << a << " " << b << " " << c << std::endl;
}
लूप से पहले घोषित चर को छिपाने का एक उदाहरण है:
int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99
लेकिन अगर आप पहले से घोषित चर का उपयोग करना चाहते हैं और इसे छिपाना नहीं चाहते हैं, तो घोषणा भाग को छोड़ दें:
int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10
टिप्पणियाँ:
- इनिशियलाइज़ेशन और इन्क्रीमेंट स्टेटमेंट्स, स्टेटमेंट स्टेटमेंट से असंबंधित ऑपरेशन कर सकते हैं, या कुछ भी नहीं - यदि आप ऐसा करना चाहते हैं। लेकिन पठनीयता कारणों के लिए, केवल लूप के लिए सीधे प्रासंगिक संचालन करना सबसे अच्छा अभ्यास है।
- एक चर प्रारंभ बयान में घोषणा की केवल के दायरे के अंदर दिखाई दे रहा है
for
पाश और पाश की समाप्ति पर जारी किया गया है। - यह मत भूलो कि
initialization statement
में जो चर घोषित किया गया था, उसे लूप के दौरान संशोधित किया जा सकता है, साथ ही साथ चर कोcondition
में जांचा जा सकता है।
एक लूप का उदाहरण जो 0 से 10 तक गिना जाता है:
for (int counter = 0; counter <= 10; ++counter)
{
std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)
कोड अंशों की व्याख्या:
-
int counter = 0
वैरिएबलcounter
को 0. से इनिशियलाइज़ करता है। (यह वेरिएबल केवल लूप केfor
इस्तेमाल किया जा सकता है।) -
counter <= 10
एक बूलियन स्थिति है जो यह जांचती है कि क्याcounter
10 से कम या बराबर है। यदि यहtrue
, तो लूप निष्पादित होता है। यदि यहfalse
, तो लूप समाप्त हो जाता है। -
++counter
एक वेतन वृद्धि ऑपरेशन है जो अगली शर्त की जांच से पहलेcounter
के मूल्य को 1 से बढ़ाता है।
सभी कथनों को खाली छोड़कर, आप एक अनंत लूप बना सकते हैं:
// infinite loop
for (;;)
std::cout << "Never ending!\n";
while
उपरोक्त लूप बराबर है:
// infinite loop
while (true)
std::cout << "Never ending!\n";
हालांकि, स्टेटमेंट break
, goto
, या return
का उपयोग करके या एक अपवाद फेंककर एक अनंत लूप को अभी भी छोड़ा जा सकता है।
<algorithm>
हेडर का उपयोग किए बिना एसटीएल संग्रह (जैसे, एक vector
) से सभी तत्वों पर पुनरावृति का अगला आम उदाहरण है:
std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
std::cout << *it << std::endl;
}
घुमाव के दौरान
एक while
पाश कार्यान्वित बयान बार-बार करने के लिए दिया हालत मूल्यांकन करता है जब तक false
। यह नियंत्रण कथन तब उपयोग किया जाता है जब यह ज्ञात नहीं होता है, अग्रिम में, कोड के एक ब्लॉक को कितनी बार निष्पादित किया जाना है।
उदाहरण के लिए, सभी नंबरों को 0 से 9 तक प्रिंट करने के लिए, निम्न कोड का उपयोग किया जा सकता है:
int i = 0;
while (i < 10)
{
std::cout << i << " ";
++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
ध्यान दें कि C ++ 17 के बाद से, पहले 2 कथनों को जोड़ा जा सकता है
while (int i = 0; i < 10)
//... The rest is the same
एक अनंत लूप बनाने के लिए, निम्नलिखित निर्माण का उपयोग किया जा सकता है:
while (true)
{
// Do something forever (however, you can exit the loop by calling 'break'
}
while
छोरों का एक और प्रकार है, अर्थात् निर्माण do...while
अधिक जानकारी के लिए करते समय लूप उदाहरण देखें।
स्थितियों में चर की घोषणा
के for
और while
छोरों की स्थिति में, इसे एक वस्तु घोषित करने की भी अनुमति है। लूप के अंत तक इस ऑब्जेक्ट को दायरे में माना जाएगा, और लूप के प्रत्येक पुनरावृत्ति के माध्यम से जारी रहेगा:
for (int i = 0; i < 5; ++i) {
do_something(i);
}
// i is no longer in scope.
for (auto& a : some_container) {
a.do_something();
}
// a is no longer in scope.
while(std::shared_ptr<Object> p = get_object()) {
p->do_something();
}
// p is no longer in scope.
हालाँकि, इसे लूप के साथ करने की अनुमति नहीं do...while
लूप; इसके बजाय, लूप से पहले वेरिएबल को घोषित करें, और (वैकल्पिक रूप से) वैरिएबल और लूप दोनों को एक स्थानीय स्कोप के साथ संलग्न करें यदि आप चाहते हैं कि लूप समाप्त होने के बाद वैरिएबल दायरे से बाहर हो जाए:
//This doesn't compile
do {
s = do_something();
} while (short s > 0);
// Good
short s;
do {
s = do_something();
} while (s > 0);
ऐसा इसलिए है क्योंकि एक do...while
के स्टेटमेंट भाग do...while
लूप (लूप की बॉडी) का मूल्यांकन एक्सप्रेशन भाग ( while
) तक पहुंचने से पहले किया जाता है, और इस प्रकार, एक्सप्रेशन का कोई भी ऐलान पहले पुनरावृति के दौरान दिखाई नहीं देगा पाश।
करते-करते पाश
एक क्या जबकि पाश, बहुत थोड़ी देर के पाश के समान है, सिवाय इसके कि हालत शुरू में नहीं प्रत्येक चक्र के अंत में चेक किया गया है,। इसलिए लूप को कम से कम एक बार निष्पादित करने की गारंटी दी जाती है।
निम्न कोड 0
प्रिंट करेगा, क्योंकि पहली पुनरावृत्ति के अंत में स्थिति false
मूल्यांकन करेगी:
int i =0;
do
{
std::cout << i;
++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console
नोट: while(condition);
के अंत में अर्धविराम को मत भूलना while(condition);
, जो निर्माण के समय में आवश्यक है।
डू-टाइम लूप के विपरीत, निम्नलिखित कुछ भी प्रिंट नहीं करेगा, क्योंकि पहली पुनरावृत्तियों की शुरुआत में स्थिति false
मूल्यांकन करती है:
int i =0;
while (i < 0)
{
std::cout << i;
++i; // Increment counter
}
std::cout << std::endl; // End of line; nothing is printed to the console
नोट: एक समय में एक break
, goto
, या return
स्टेटमेंट का उपयोग करके लूप को शर्त के बिना बाहर निकाला जा सकता है।
int i = 0;
do
{
std::cout << i;
++i; // Increment counter
if (i > 5)
{
break;
}
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console
एक तुच्छ काम करते समय लूप को कभी-कभी मैक्रोज़ लिखने के लिए भी उपयोग किया जाता है, जिसमें उनके स्वयं के दायरे की आवश्यकता होती है (जिस स्थिति में अनुगामी अर्धविराम को स्थूल परिभाषा से हटा दिया जाता है और उपयोगकर्ता द्वारा प्रदान किए जाने की आवश्यकता होती है):
#define BAD_MACRO(x) f1(x); f2(x); f3(x);
// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);
#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)
// All calls are protected here
if (cond) GOOD_MACRO(var);
लूप नियंत्रण कथन: विराम और जारी रखें
इसके सामान्य अनुक्रम से निष्पादन के प्रवाह को बदलने के लिए लूप नियंत्रण बयानों का उपयोग किया जाता है। जब निष्पादन एक गुंजाइश छोड़ देता है, तो उस दायरे में बनाए गए सभी स्वचालित ऑब्जेक्ट नष्ट हो जाते हैं। break
और continue
लूप नियंत्रण कथन हैं।
break
कथन बिना किसी और विचार के एक लूप को समाप्त कर break
।
for (int i = 0; i < 10; i++)
{
if (i == 4)
break; // this will immediately exit our loop
std::cout << i << '\n';
}
उपरोक्त कोड प्रिंट होगा:
1
2
3
continue
बयान तुरंत लूप से बाहर नहीं निकलता है, बल्कि बाकी लूप शरीर को छोड़ देता है और लूप के शीर्ष पर जाता है (स्थिति की जांच सहित)।
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0) // evaluates to true if i is even
continue; // this will immediately go back to the start of the loop
/* the next line will only be reached if the above "continue" statement
does not execute */
std::cout << i << " is an odd number\n";
}
उपरोक्त कोड प्रिंट होगा:
1 is an odd number
3 is an odd number
5 is an odd number
क्योंकि इस तरह के नियंत्रण प्रवाह परिवर्तन कभी-कभी मनुष्यों को आसानी से समझने, break
और continue
लिए मुश्किल से उपयोग किए जाते हैं। अधिक सरल कार्यान्वयन आमतौर पर पढ़ने और समझने में आसान होते हैं। उदाहरण के लिए, ऊपर के break
साथ लूप के for
पहले for
फिर से लिखा जा सकता है:
for (int i = 0; i < 4; i++)
{
std::cout << i << '\n';
}
continue
साथ दूसरा उदाहरण फिर से लिखा जा सकता है:
for (int i = 0; i < 6; i++)
{
if (i % 2 != 0) {
std::cout << i << " is an odd number\n";
}
}
रेंज-के लिए एक उप-रेंज
रेंज-बेस लूप का उपयोग करते हुए, आप किसी दिए गए कंटेनर या अन्य श्रेणी के उप-भाग पर एक प्रॉक्सी ऑब्जेक्ट उत्पन्न करके लूप कर सकते हैं जो लूप के लिए रेंज-आधारित के लिए अर्हता प्राप्त करता है।
template<class Iterator, class Sentinel=Iterator>
struct range_t {
Iterator b;
Sentinel e;
Iterator begin() const { return b; }
Sentinel end() const { return e; }
bool empty() const { return begin()==end(); }
range_t without_front( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {std::next(b, count), e};
}
range_t without_back( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {b, std::prev(e, count)};
}
};
template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
using std::begin; using std::end;
return range(begin(r),end(r));
}
template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}
अब हम कर सकते हैं:
std::vector<int> v = {1,2,3,4};
for (auto i : except_first(v))
std::cout << i << '\n';
और प्रिंट आउट लें
2
3
4
ध्यान रखें कि लूप के for
उत्पन्न मध्यवर्ती ऑब्जेक्ट for(:range_expression)
भाग में लूप शुरू for
के समय तक समाप्त हो जाएगा।