В настоящата глава ще разгледаме вложените проверки в езика C++, чрез които нашата програма може да съдържа условни конструкции, в които има вложени други условни конструкции. Наричаме ги "вложени", защото поставяме if
конструкция в друга if
конструкция. Ще разгледаме и по-сложни логически условия с подходящи примери.
Доста често програмната логика налага използването на if
или if-else
конструкции, които се съдържат една в друга. Те биват наричани вложени if
или if-else
конструкции. Както се подразбира от названието "вложени", това са if
или if-else
конструкции, които са поставени в други if
или else
конструкции.
if (условие1) {
if (условие2) {
// тяло
}
else {
// тяло
}
}
Според въведени възраст (десетично число) и пол (m / f) да се отпечата обръщение:
- “Mr.” – мъж (пол “m”) на 16 или повече години.
- “Master” – момче (пол “m”) под 16 години.
- “Ms.” – жена (пол “f”) на 16 или повече години.
- “Miss” – момиче (пол “f”) под 16 години.
Вход | Изход | Вход | Изход |
---|---|---|---|
12 f |
Miss | 17 m |
Mr. |
Вход | Изход | Вход | Изход |
---|---|---|---|
25 f |
Ms. | 13.5 m |
Master |
Можем да забележим, че изходът на програмата зависи от няколко неща. Първо трябва да проверим какъв пол е въведен и после да проверим възрастта. Съответно ще използваме няколко if-else
блока. Тези блокове ще бъдат вложени, т.е. от резултата на първия ще се определи кои от другите да се изпълни:
След прочитане на входните данни от конзолата ще трябва да се изпълни следната примерна програмна логика:
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#0.
Предприемчив българин отваря по едно квартално магазинче в няколко града с различни цени за следните продукти:
продукт | Sofia | Plovdiv | Varna |
---|---|---|---|
coffee | 0.50 | 0.40 | 0.45 |
water | 0.80 | 0.70 | 0.70 |
beer | 1.20 | 1.15 | 1.10 |
sweets | 1.45 | 1.30 | 1.35 |
peanuts | 1.60 | 1.50 | 1.55 |
По дадени град (стринг), продукт (стринг) и количество (десетично число), да се пресметне цената ѝ.
Вход | Изход | Вход | Изход |
---|---|---|---|
coffee Varna 2 |
0.9 | peanuts Plovdiv 1 |
1.5 |
Вход | Изход | Вход | Изход |
---|---|---|---|
beer Sofia 6 |
7.2 | water Plovdiv 3 |
2.1 |
Примерен алгоритъм за решение:
Кодът е недовършен с цел да се стимулира самостоятелно мислене и решение.
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#1.
Нека разгледаме как можем да правим по-сложни логически проверки. Може да използваме логическо "И" (&&
), логическо "ИЛИ" (||
), логическо отрицание (!
) и скоби (()
).
Както видяхме, в някои задачи се налага да правим много проверки наведнъж. Но какво става, когато за да изпълним някакъв код, трябва да бъдат изпълнени няколко условия едновременно и не искаме да правим отрицание (else
) за всяко едно от тях? Вариантът с вложените if
блокове е валиден, но кодът би изглеждал много неподреден и със сигурност - труден за четене и поддръжка.
Логическо "И" (оператор &&
) означава няколко условия да са изпълнени едновременно. В сила е следната таблица на истинност:
a | b | a && b |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
Операторът &&
приема няколко булеви (условни) израза, които имат стойност true
или false
, и ни връща един булев израз като резултат (0 за false
и 1 за true
). Използването му вместо редица вложени if
блокове прави кода по-четлив, подреден и лесен за поддръжка. Но как работи, когато поставим няколко условия едно след друго? Както видяхме по-горе, логическото "И" връща true
, само когато приема като аргументи изрази със стойност true
. Съответно, когато имаме последователност от аргументи, логическото "И" проверява или докато свършат аргументите, или докато не срещне аргумент със стойност false
.
Пример:
bool a = true;
bool b = true;
bool c = false;
bool d = true;
bool result = a && b && c && d;
// 0 - false (като d не се проверява)
Може да тествате примера онлайн: https://repl.it/@vncpetrov/OperatorAND.
Програмата ще се изпълни по следния начин: започва проверката от а
, прочита я и отчита, че има стойност true
, след което проверява b
. След като е отчела, че a
и b
връщат стойност true
, проверява следващия аргумент. Стига до c
и отчита, че променливата има стойност false
. След като програмата отчете, че аргументът c
има стойност false
, тя изчислява израза до c
, независимо каква е стойността на d
. За това проверката на d
се прескача и целият израз бива изчислен като false
.
Проверка дали точка {x, y} се намира вътре в правоъгълника {x1, y1} – {x2, y2}. Входните данни се четат от конзолата и се състоят от 6 реда: десетичните числа x1, y1, x2, y2, x и y (като се гарантира, че x1 < x2 и y1 < y2).
Вход | Изход | Визуализация |
---|---|---|
2 -3 12 3 8 -1 |
Inside | ![]() |
Една точка е вътрешна за даден многоъгълник, ако едновременно са изпълнени следните четири условия:
- Точката е вдясно от лявата страна на правоъгълника.
- Точката е вляво от дясната страна на правоъгълника.
- Точката е под горната страна на правоъгълника.
- Точката е над долната страна на правоъгълника.
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#2.
Логическо "ИЛИ" (оператор ||
) означава да е изпълнено поне едно измежду няколко условия. Подобно на оператора &&
, логическото "ИЛИ" приема няколко аргумента от булев (условен) тип и връща true
или false
. Лесно можем да се досетим, че получаваме като стойност true
, винаги когато поне един от аргументите има стойност true
. Типичен пример за логиката на този оператор е следният:
В училище учителят казва: "Иван или Петър да измият дъската". За да бъде изпълнено това условие (дъската да бъде измита), е възможно само Иван да я измие, само Петър да я измие или и двамата да го направят.
a | b | a || b |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
Вече научихме какво представлява логическото "ИЛИ". Но как всъщност се реализира? Както при логическото "И", програмата проверява от ляво на дясно аргументите, които са зададени. За да получим true
от израза, е необходимо само един аргумент да има стойност true
, съответно проверката продължава докато се срещне аргумент с такава стойност или докато не свършат аргументите.
Ето един пример за оператора ||
в действие:
bool a = false;
bool b = false;
bool c = true;
bool d = true;
bool result = a || b || c || d;
// 1 - true (като c и d не се проверяват)
Може да тествате примера онлайн: https://repl.it/@vncpetrov/OperatorOr.
Програмата проверява а
, отчита, че има стойност false
и продължава. Стигайки до b
, отчита, че има стойност true
и целият израз получава стойност true
, без да се проверява c
и d
, защото техните стойности не биха променили резултата на израза.
Нека проверим дали даден продукт е плод или зеленчук. Плодовете "fruit" са banana, apple, kiwi, cherry, lemon и grapes. Зеленчуците "vegetable" са tomato, cucumber, pepper и carrot. Всички останали са "unknown".
Вход | Изход |
---|---|
banana tomato java |
fruit vegetable unknown |
Трябва да използваме няколко условни проверки с логическо "ИЛИ" (||
):
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#3.
Логическо отрицание (оператор !) означава да не е изпълнено дадено условие.
a | !a |
---|---|
true | false |
false | true |
Операторът !
приема като аргумент булева променлива и обръща стойността ѝ (истината стажа лъжа, а лъжата става истина).
Дадено число е валидно, ако е в диапазона [100 … 200] или е 0. Да се направи проверка за невалидно число.
Вход | Изход |
---|---|
75 | invalid |
150 | (няма изход) |
220 | invalid |
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#4.
Както останалите оператори в програмирането, така и операторите &&
и ||
имат приоритет, като в случая &&
е с по-голям приоритет от ||
. Операторът ()
служи за промяна на приоритета на операторите и се изчислява пръв, също както в математиката. Използването на скоби също така придава по-добра четимост на кода и се счита за добра практика.
Понякога условията може да са доста сложни, така че да изискват дълъг булев израз или поредица от проверки. Да разгледаме няколко такива примера.
Да се напише програма, която проверява дали точка {x, y} се намира върху някоя от страните на правоъгълник {x1, y1} - {x2, y2}. Входните данни се четат от конзолата и се състоят от 6 реда: десетичните числа x1, y1, x2, y2, x и y (като се гарантира, че x1 < x2 и y1 < y2). Да се отпечата "Border" (точката лежи на някоя от страните) или "Inside / Outside" (в противен случай).
Вход | Изход | Вход | Изход |
---|---|---|---|
2 -3 12 3 12 -1 |
Border | 2 -3 12 3 8 -1 |
Inside / Outside |
Точка лежи върху някоя от страните на правоъгълник, ако:
- x съвпада с x1 или x2 и същевременно y е между y1 и y2 или
- y съвпада с y1 или y2 и същевременно x е между x1 и x2.
Предходната проверка може да се опрости по този начин:
Вторият начин с допълнителните булеви променливи е по-дълъг, но е много по-разбираем от първия, нали? Препоръчваме ви когато пишете булеви условия, да ги правите лесни за четене и разбиране, а не кратки. Ако се налага, ползвайте допълнителни променливи със смислени имена. Имената на булевите променливи трябва да подсказват каква стойност се съхранява в тях.
Остава да се допише кода, за да отпечатва "Inside / Outside", ако точката не е върху някоя от страните на правоъгълника.
След като допишете решението, може да го тествате тук: https://judge.softuni.org/Contests/Practice/Index/1362#5.
Магазин за плодове в работни дни продава на следните цени:
Плод | Цена |
---|---|
banana | 2.50 |
apple | 1.20 |
orange | 0.85 |
grapefruit | 1.45 |
kiwi | 2.70 |
pineapple | 5.50 |
grapes | 3.85 |
В почивни дни цените са по-високи:
Плод | Цена |
---|---|
banana | 2.70 |
apple | 1.25 |
orange | 0.90 |
grapefruit | 1.60 |
kiwi | 3.00 |
pineapple | 5.60 |
grapes | 4.20 |
Напишете програма, която чете от конзолата плод (banana / apple / …), ден от седмицата (Monday / Tuesday / …) и количество (десетично число) и пресмята цената според цените от таблиците по-горе. Резултатът да се отпечата закръглен с 2 цифри след десетичния знак. При невалиден ден от седмицата или невалидно име на плод да се отпечата "error".
Вход | Изход | Вход | Изход |
---|---|---|---|
orange Sunday 3 |
2.70 | kiwi Monday 2.5 |
6.75 |
Вход | Изход | Вход | Изход |
---|---|---|---|
grapes Saturday 0.5 |
2.10 | tomato Monday 0.5 |
error |
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#6.
Фирма дава следните комисионни на търговците си според града, в който работят и обема на продажбите s:
Град | 0 <= s <= 500 | 500 < s <= 1000 | 1000 < s <= 10000 | s > 10000 |
---|---|---|---|---|
Sofia | 5% | 7% | 8% | 12% |
Varna | 4.5% | 7.5% | 10% | 13% |
Plovdiv | 5.5% | 8% | 12% | 14.5% |
Напишете програма, която чете име на град (стринг) и обем на продажбите (десетично число) и изчислява размера на комисионната. Резултатът да се изведе закръглен с 2 десетични цифри след десетичния знак. При невалиден град или обем на продажбите (отрицателно число) да се отпечата "error".
Вход | Изход | Вход | Изход | Вход | Изход |
---|---|---|---|---|---|
Sofia 1500 |
120.00 | Plovdiv 499.99 |
27.50 | Kaspichan -50 |
error |
Първоначално задаваме комисионната да е -1
. Тя ще бъде променена, ако градът и ценовият диапазон бъдат намерени в таблицата с комисионните. За да изчислим комисионната според града и обема на продажбите се нуждаем от няколко вложени if
проверки, както е в примерния код по-долу:
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#7.
Конструкцията switch-case
работи като поредица if-else
блокове. Когато работата на програмата ни зависи от стойността на една променлива, вместо да правим последователни проверки с if-else
блокове, можем да използваме условната конструкция switch
. Тя се използва за избор измежду списък с възможности. Конструкцията сравнява дадена стойност с определени константи и в зависимост от резултата предприема действие.
Променливата, която искаме да сравняваме, поставяме в скобите след оператора switch
и се нарича "селектор". Тук типът трябва да е сравним. Последователно започва сравняването с всяка една стойност, която се намира след case
етикетите. При съвпадение започва изпълнението на кода от съответното място и продължава, докато стигне оператора break
. В C++ и някои други програмни езици break
може да се изпуска, за да се изпълнява код от друга case
конструкция, докато не стигне до въпросния оператор. При липса на съвпадение, се изпълнява default
конструкцията, ако такава съществува.
switch (селектор) {
case стойност1:
конструкция;
break;
case стойност2:
конструкция;
break;
case стойност3:
конструкция;
break;
…
default:
конструкция;
break;
}
В C++ селекторът и стойностите в case
етикетите трябва да са цели числа (int
). За разлика от други езици за програмиране, C++ не позволява използването на конструкция switch-case
с низове (string
), или дробни числа (double
).
Нека напишем програма, която принтира деня от седмицата (на английски) според въведеното число (1 … 7) или "Error!", ако е въведено невалидно число.
Вход | Изход |
---|---|
1 | Monday |
7 | Sunday |
-1 | Error! |
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#8.
Напишете програма, която принтира вида на животно според името му:
- dog -> mammal
- crocodile, tortoise, snake -> reptile
- others -> unknown
Вход | Изход | Вход | Изход | Вход | Изход |
---|---|---|---|---|---|
tortoise | reptile | dog | mammal | elephant | unknown |
В тази задача не можем да използваме switch-case
, тъй като трябва да сравняваме низове, а не цели числа. Можем да я решим чрез няколко if-else
проверки:
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#9.
Да си припомним новите конструкции и програмни техники, с които се запознахме в тази глава:
if (условие1) {
if (условие2) {
// тяло
}
else {
// тяло
}
}
if (((x == left) || (x == right))
&& (y >= top) && (y <= bottom)) {
// тяло
}
switch (селектор)
{
case стойност1:
конструкция;
break;
case стойност2:
конструкция;
break;
case стойност3:
конструкция;
break;
…
default:
конструкция;
break;
}
Нека сега да упражним работата с по-сложни проверки. Да решим няколко практически задачи.
В една кинозала столовете са наредени в правоъгълна форма в rows реда и columns колони. Има три вида прожекции с билети на различни цени:
- Premiere – премиерна прожекция, на цена 12.00 лева.
- Normal – стандартна прожекция, на цена 7.50 лева.
- Discount – прожекция за деца, ученици и студенти на намалена цена от 5.00 лева.
Напишете програма, която въвежда тип прожекция (стринг), брой редове и брой колони в залата (цели числа) и изчислява общите приходи от билети при пълна зала. Резултатът да се отпечата във формат като в примерите по-долу - с 2 цифри след десетичния знак.
Вход | Изход | Вход | Изход |
---|---|---|---|
Premiere 10 12 |
1440.00 leva | Normal 21 13 |
2047.50 leva |
Прочитаме входните данни. След което, създаваме и инициализираме променлива, която ще ни съхранява изчислените приходи. В друга променлива пресмятаме пълния капацитет на залата. Тъй като тук не можем да използваме условната конструкция switch-case
, ще използваме if-else
, за да изчислим прихода в зависимост от вида на прожекцията и отпечатваме резултата на конзолата в зададения формат (потърсете нужната C++ функционалност в интернет).
При прочитането на входа можем да обърнем типа на прожекцията в малки букви (с функцията .lower()
). Създаваме и променлива, която ще ни съхранява изчислените приходи. В друга променлива пресмятаме пълния капацитет на залата. Използваме if-elif
условна конструкция, за да изчислим прихода в зависимост от вида на прожекцията и отпечатваме резултата на конзолата в зададения формат (потърсете нужната C++ функционалност в интернет).
Примерен код на описаната идея (части от кода са замъглени с цел да се стимулира самостоятелно мислене и решение):
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#10.
Влади е студент, живее в София и си ходи от време на време до родния град. Той е много запален по волейбола, но е зает през работните дни и играе волейбол само през уикендите и в празничните дни. Влади играе в София всяка събота, когато не е на работа и не си пътува до родния град, както и в 2/3 от празничните дни. Той пътува до родния си град h пъти в годината, където играе волейбол със старите си приятели в неделя. Влади не е на работа 3/4 от уикендите, в които е в София. Отделно, през високосните години Влади играе с 15% повече волейбол от нормалното. Приемаме, че годината има точно 48 уикенда, подходящи за волейбол. Напишете програма, която изчислява колко пъти Влади е играл волейбол през годината. Закръглете резултата надолу до най-близкото цяло число (напр. 2.15 -> 2; 9.95 -> 9).
Входните данни се четат от конзолата:
- Първият ред съдържа думата "leap" (високосна година) или "normal" (нормална година с 365 дни).
- Вторият ред съдържа цялото число p – брой празници в годината (които не са събота или неделя).
- Третият ред съдържа цялото число h – брой уикенди, в които Влади си пътува до родния град.
Вход | Изход | Вход | Изход |
---|---|---|---|
leap 5 2 |
45 | normal 3 2 |
38 |
Вход | Изход | Вход | Изход |
---|---|---|---|
normal 11 6 |
44 | leap 0 1 |
41 |
Стандартно прочитаме входните данни от конзолата. Последователно пресмятаме уикендите прекарани в София, времето за игра в София и общото време за игра. Накрая проверяваме дали годината е високосна, правим допълнителни изчисления при необходимост и извеждаме резултата на конзолата, закръглен надолу до най-близкото цяло число (потърсете C++ функция с такава функционалност в интернет).
Примерен код на описаната идея (части от кода са замъглени с цел да се стимулира самостоятелно мислене и решение):
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#11.
Фигура се състои от 6 блокчета с размер h * h, разположени като на фигурата. Долният ляв ъгъл на сградата е на позиция {0, 0}. Горният десен ъгъл на фигурата е на позиция {2*h, 4*h}. На фигурата координатите са дадени при h = 2:
Да се напише програма, която въвежда цяло число h и координатите на дадена точка {x, y} (цели числа) и отпечатва дали точката е вътре във фигурата (inside), вън от фигурата (outside) или на някоя от стените на фигурата (border).
Вход | Изход | Вход | Изход |
---|---|---|---|
2 3 10 |
outside | 2 3 1 |
inside |
2 2 2 |
border | 2 6 0 |
border |
2 0 6 |
outside | 15 13 55 |
outside |
15 29 37 |
inside | 15 37 18 |
outside |
15 -4 7 |
outside | 15 30 0 |
border |
Примерна логика за решаване на задачата (не е единствената правилна):
- Може да разделим фигурата на два правоъгълника с обща стена:
- Една точка е външна (outside) за фигурата, когато е едновременно извън двата правоъгълника.
- Една точка е вътрешна (inside) за фигурата, ако е вътре в някой от правоъгълниците (изключвайки стените им) или лежи върху общата им стена.
- В противен случай точката лежи на стената на правоъгълника (border).
Примерен код (части от кода са замъглени с цел да се стимулира самостоятелно мислене и решение):
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1362#12.