Ճյուղավորում և ցիկլեր
Մինչ այս պահը դիտարկում էինք ծրագրեր, որոնց վարքը գծային էր․ բոլոր հրամանները, առանց բացառության, կատարվում էին առաջինից մինչև վերջինը՝ միայն մեկ անգամ։ Սակայն գործնականում շատ հազվադեպ են հանդիպում զուտ գծային ալգորիթմներ։ Համարյա միշտ ծրագրավորողին պետք է լինում հրամանների շարքը կատարել մի ինչ֊որ պայմանից կախված, կամ կրկնել մի քանի անգամ։
Օրինակ, ուզում ենք տեղեկացնել, որ ստեղնաշարից ներմուծված x
փոփոխականի արժեքը դրական է։ Այսինքն՝ «x֊ը դրական է։
» տեքստն արտածել միայն այն դեպքում, երբ ճշմարիտ է պայմանը։
int x = 0;
std::cin >> x;
if( x >= 0 )
std::cout << x << "-ը դրական է։\n";
if
ծառայողական բառով սկսվում է C++ լեզվի պայմանական կամ ճյուղավորման հրամանը։ Այն թույլ է տալիս հրամանի (կամ հրամանների բլոկի) կատարվելը ղեկավարել տրված պայմանով։ if
հրամանը թույլ է տալիս գործողություններ նկարագրել նաև այն դեպքի համար, երբ տրված պայմանը ճշմարիտ չէ (կեղծ է)։ Այսպես.
int x = 0;
std::cin >> x;
if( x >= 0 )
std::cout << x << "-ը դրական է։\n";
else
std::cout << x << "-ը բացասական է։\n";
Ընդհանուր դեպքում եթե բանաձևով ներկայացնենք if
հրամանը, ապա այն կունենա հետևյալ տեսքը.
if( ⟨condition⟩ ) ⟨statements-1⟩ else ⟨statements-2⟩
Եթե ճշմարիտ է ⟨condition⟩
պայմանը, ապա կատարվում է հրամանների ⟨statements-1⟩
շարքը, հակառակ դեպքում կատարվում է else
ծառայողական բառից հետո գրված ⟨statements-2⟩
շարքը։ Արդեն նկատեցինք նաև, որ if
հրամանի else
ճյուղը պարտադիր չէ։
⟨statements-1⟩
-ը և ⟨statements-2⟩
-ը կարող են լինել կամայական հրամաններ կամ հրամանների շարք։ Դա նաև հուշում է, որ դրանց տեղում կարող է հանդիպել նույն if
հրամանը՝ թույլ տալով կառուցել պայմանների շարք։ Օրինակ, ուզում ենք ստուգել, թե ո՛ր կոորդինատական քառորդում է գտնվում տրված կետը։
double x = 0, y = 0;
std::cin >> x >> y;
if( x >= 0 && y >= 0 )
std::cout << "առաջին\n";
else if( x >= 0 && y < 0 )
std::cout << "չորրորդ\n";
else if( x < 0 && y >= 0 )
std::cout << "երկրորդ\n";
else if( x < 0 && y >= 0 )
std::cout << "երրորդ\n";
Այս կոնկրետ դեպքում, քանի որ հնարավոր դեպքերը ճիշտ չորսն են, կարելի է բաց թողնել վերջին պայմանի ստուգումը։
Երկու արժեքների մեջ ընտրություն կատարելու համար հաճախ օգտագործվում է «?:
» տերնար (եռատեղ) գործողությունը․ ex0 ? val1 : val2
։ Եթե ճիշտ է ex0
պայմանը, ապա արտահայտության արժեքը val1
է, հակառակ դեպքում՝ val2
է։ Օրինակ, c
փոփոխականին a
և b
արժեքներից առավել մեծը վերագրելու համար կարող ենք գրել ինչպես if
հրամանը․
if( a > b ) c = a; else c = b;
այնպես էլ ?:
գործողությունը․
c = a > b ? a : b;
Հատուկ նշենք, որ ի տարբերություն if
֊ի, ?:
֊ը ոչ թե հրաման է, այլ՝ գործողություն․ այն կարելի է օգտագրծել միայն արտահայտությունների մեջ։
Ճյուղավորման մասին առայժմ այսքանը։ Հիմա տեսնենք, թե ինչ միջոցներ է առաջարկում C++ լեզուն այն դեպքերի համար, երբ հարկավոր է ինչ որ հրամաններ կրկնել մեկից ավելի անգամներ՝ գրել ցիկլիկ ալգորիթմներ։ Օրինակ, թող որ պահանջվում է գրել digitSum
ֆունկցիան, որը վերադարձնում է տրված ամբողջ թվի թվանշանների գումարը։ Թվի թվանշաններն առանձնացնելը հարմար է վերջից։ Պարզ է, որ, օրինակ, 2016
թվից 6
-ը առանձնացնելու համար պետք է այն բաժանել 10
-ի և վերցնել մնացորդը։ Հետո, 1
նիշը ստանալու համար պետք է նախորդ քայլի բաժանումից ստացված քանորդը նորից բաժանել 10
-ի և վերցնել քանորդը։ Այսպես՝ քանի դեռ բաժանման քանորդը զրո չէ։
Մի որևէ պայմանով, իսկ մեր դեպքում պայմանը բաժանման հերթական քանորդի զրո չլինելն է, ցիկլերը կազմակերպվում են while
հրամանով։ Դրա բանաձևային տեսքն այսպիսինն է. «քանի դեռ ճշմարիտ է ⟨condition⟩
պայմանը, պետք է կատարել ⟨statements⟩
հրամանները»․
while( ⟨condition⟩ ) ⟨statements⟩
Վերջապես սահմանենք digitSum
ֆունկցիան.
int digitSum( unsigned long int num )
{
int sum = 0;
while( num != 0 ) {
sum += num % 10;
num /= 10;
}
return sum;
}
while
կառուցվածքի օգտագործման դեպքում նախ ստուգվում է ցիկլի պայմանը, ապա, եթե այն ճշմարիտ է, կատարվում են ցիկլի մարմնի հրամանները։ Այսպիսի ցիկլերը կոչվում են նախապայմանով ցիկլեր։ C++ լեզվի do-while
կառուցվածքը թույլ է տալիս կազմակերպել նաև ետպայմանով ցիկլեր, որում նախ կատարվում է ցիկլի մարմինը, ապա ստուգվում է պայմանը։ Օրինակ,
C++ լեզվում նախատեսված է նաև պարամետրով ցիկլերի կազմակերպման for
կաառուցվածքը։ for
հրամանը ծրագրավորողին հնարավորություն է տալիս ցիկլի մարմնում ունենալ լոկալ պարամետր։ Օրինակ, եթե ուզում ենք տրված տողում հաշվել բացատանիշերի քանակը, ապա հարմար է ունենալ մի i
ինդեքս, որի օգնությամբ կդիմենք տողի հերթական նիշին։ Այսպես.
int countSpaces( const string& text )
{
int count = 0;
for( int i = 0; i < text.length(); ++i )
if( text[i] == ' ' )
++count;
return count;
}
Այս ֆունկցիայում օգտագործվել է for
հրամանը, որի ընդհանուր տեսքը հետևյալն է.
for( ⟨initialize⟩; ⟨condition⟩; ⟨increment⟩ )
⟨statements⟩
Նախ հաշվարկվում է ⟨initialize⟩
արտահայտությունը, հետո ստուգվում է ⟨condition⟩
պայմանը, և եթե այն ճշմարիտ է, ապա կատարվում են ցիկլի մարմնի ⟨statements⟩
հրամանները։ Այնուհետև հաշվարկվում է ⟨increment⟩
արտահայտությունը, որը սովորաբար ազդեցություն է թողնում ⟨condition⟩
-ի վրա։ Ավելի պատկերավոր ասած՝ for
հրամանը կարող ենք գրառել while
հրամանի օգնությամբ.
⟨initialize⟩
while( ⟨condition⟩ ) {
⟨statements⟩
⟨increment⟩
}
Որքան էլ որ տարօրինակ կթվա՝ ճյուղավորման հրամանով հնարավոր է ծրագրավրել նաև կրկնություններ։ Օրինակ, գրենք մի ֆունկցիա, որը հաշվում է Ֆիբոնաչիի շարքի n֊րդ անդամը։ Հիշենք, որ այդ շարքի առաջին երկու անդամները 1 են, իսկ ամեն մի հաջորդը իր նախորդ երկուսի գումարն է․
C++ լեզվով այս բանաձևի գրառումը համարյա նույնական է․
int f( int n )
{
if( n == 1 || n == 2 )
return 1;
return f(n-1) + f(n-2);
}
հաշվում է տրված ամբողջ թվի բաժանարարների (բացի իրենից) գումարը։
int sum_divisors( int n, int k )
{
if( k = 1 )
return 1;
if( n % k == 0 )
return k + sum_divisors( n, k - 1 );
return sum_divisors( n, k - 1 );
}
!!! Երկուսն էլ անհաջող օրինակներ են !!!
Այսպիսի ֆունկցիան, որի մարմնում կանչ է կատարվում ինքն իրեն, կոչվում է ռեկուրսիվ (անդրադարձ) ֆունկցիա։