С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 34
Текст из файла (страница 34)
На самом же делеинструкцияoccurs = 1;не является частью if и выполняется безусловно, всегда сбрасывая occurs в 1. Вот какдолжна быть составлена правильная if-инструкция (точное положение открывающейфигурной скобки является поводом для бесконечных споров):189С++ для начинающихif ( minVal > ivec[ i ] ){minVal = ivec[ i ];occurs = 1;}if ( minVal == ivec [ i ] )Вторая инструкция if выглядит так:++occurs;Заметим, что порядок следования инструкций в этом примере крайне важен. Если мыбудем сравнивать minVal именно в такой последовательности, наша функция всегдаif ( minVal > ivec[ i ] ) {minVal = ivec[ i ];occurs = 1;}//если minVal только что получила новое значение,//то occurs будет на единицу больше, чем нужноif( minVal == ivec[ i ] )будет ошибаться на 1:++occurs;Выполнение второго сравнения не обязательно: один и тот же элемент не можетодновременно быть и меньше и равен minVal.
Поэтому появляется необходимостьвыбора одного из двух блоков в зависимости от условия, что реализуется инструкциейif ( условие )инструкция1elseif-else, второй формой if-инструкции. Ее синтаксис выглядит таким образом:инструкция2инструкция1 выполняется, если условие истинно, иначе переходим к инструкция2.if ( minVal == ivec[ i ] )++occurs;elseif ( minVal > ivec[ i ] ) {minVal = ivec[ i ];occurs = 1;Например:}190С++ для начинающихЗдесь инструкция2 сама является if-инструкцией.
Если minVal меньше ivec[i],никаких действий не производится.if ( minVal < ivec[ i ] ){} // пустая инструкцияelseif ( minVal > ivec[ i ] ) {minVal = ivec[ i ];occurs = 1;}else // minVal == ivec[ i ]В следующем примере выполняется одна из трех инструкций:++occurs;Составные инструкции if-else могут служить источником неоднозначного толкования,если частей else больше, чем частей if. К какому из if отнести данную часть else?if ( minVal <= ivec[ i ] )if ( minVal == ivec[ i ] )++occurs;else {minVal = ivec[ i ];occurs = 1;(Эту проблему иногда называют проблемой висячего else). Например:}Судя по отступам, программист предполагает, что else относится к самому первому,внешнему if. Однако в С++ неоднозначность висячих else разрешается соотнесением ихс последним встретившимся if. Таким образом, в действительности предыдущийif ( minVal <= ivec[ i ] ) {if ( minVal == ivec[ i ] )++occurs;else {minVal= ivec[ i ];occurs= 1;}фрагмент означает следующее:}Одним из способов разрешения данной проблемы является заключение внутреннего if вфигурные скобки:191С++ для начинающихif ( minVal <= ivec[ i ] ) {if ( minVal == ivec[ i ] )++occurs;}else {minVal = ivec[ i ];occurs = 1;}В некоторых стилях программирования рекомендуется всегда употреблять фигурныескобки при использовании инструкций if-else, чтобы не допустить возможностинеправильной интерпретации кода.Вот первый вариант функции min().
Второй аргумент функции будет возвращатьколичество вхождений минимального значения в вектор. Для перебора элементовмассива используется цикл for. Но мы допустили ошибку в логике программы. Сможете#include <vector>int min( const vector<int> &ivec, int &occurs ){int minVal = 0;occurs = 0;int size = ivec.size();for ( int ix = 0; ix < size; ++ix ) {if ( minVal == ivec[ ix ] )++occurs;elseif ( minVal > ivec[ ix ] ) {minVal = ivec[ ix ];occurs = 1;}}return minVal;ли вы заметить ее?}Обычно функция возвращает только одно значение. Однако согласно нашейспецификации в точке вызова должно быть известно не только само минимальноезначение, но и количество его вхождений в вектор. Для возврата второго значения мыиспользовали параметр типа ссылка.
(Параметры-ссылки рассматриваются в разделе 7.3.)Любое присваивание значения ссылке occurs изменяет значение переменной, накоторую она ссылается:192С++ для начинающихint main(){int occur_cnt = 0;vector< int > ivec;// occur_cnt получает значение occurs// из функции min()int minval = min( ivec, occur_cnt );// ...}Альтернативой использованию параметра-ссылки является применение объекта классаpair, представленного в разделе 3.14. Функция min() могла бы возвращать два значения// альтернативная реализация// с помощью пары#include <uti1ity>#include <vector>typedef pair<int,int> min_va1_pair;min_va1_pairmin( const vector<int> &ivec ){int minVal = 0;int occurs = 0;// то же самое ...return make_pair( minVal, occurs );в одной паре:}К сожалению, и эта реализация содержит ошибку.
Где же она? Правильно: мыинициализировали minVal нулем, поэтому, если минимальный элемент вектора большенуля, наша реализация вернет нулевое значение минимума и нулевое значениеколичества вхождений.Программу можно изменить, инициализировав minVal первым элементом вектора:int minVal = ivec[0];Теперь функция работает правильно. Однако в ней выполняются некоторые лишниедействия, снижающие ее эффективность.193С++ для начинающих// исправленная версия min()// оставляющая возможность для оптимизации ...int minVal = ivec[0];occurs = 0;intsize = ivec.size();for{( int ix = 0; ix < size; ++ix )if ( minVal == ivec[ ix ] )++occurs;// ...Поскольку ix инициализируется нулем, на первой итерации цикла значение первогоэлемента сравнивается с самим собой.
Можно инициализировать ix единицей и избежатьненужного выполнения первой итерации. Однако при оптимизации кода мы допустилидругую ошибку (наверное, стоило все оставить как было!). Сможете ли вы ее// оптимизированная версия min(),// к сожалению, содержащая ошибку...int minVal = ivec[0];occurs = 0;intsize = ivec.size();for{( int ix = 1; ix < size; ++ix )if ( minVal == ivec[ ix ] )++occurs;обнаружить?// ...Если ivec[0] окажется минимальным элементом, переменная occurs не получитint minVal = ivec[0];значения 1. Конечно, исправить это очень просто, но сначала надо найти ошибку:occurs = 1;К сожалению, подобного рода недосмотры встречаются не так уж редко: программистытоже люди и могут ошибаться.
Важно понимать, что это неизбежно, и быть готовымтщательно тестировать и анализировать свои программы.Вот окончательная версия функции min() и программа main(), проверяющая ее работу:194С++ для начинающих#include <iostream>#include <vector>int min( const vector< int > &ivec, int &occurs ){int minVal = ivec[ 0 ];occurs = 1;int size = ivec.size();for ( int ix = 1; ix < size; ++ix ){if ( minVal == ivec[ ix ] )++occurs;elseif ( minVal > ivec[ ix ] ){minVal = ivec[ ix ];occurs = 1;}}return minVal;}int main(){int ia[] = { 9,1,7,1,4,8,1,3,7,2,6,1,5,1 };vector<int> ivec( ia, ia+14 );int occurs = 0;int minVal = min( ivec, occurs );cout << "Минимальное значение: " << minVal<< " встречается: " << occurs << " раз.\n";return 0;}Результат работы программы:Минимальное значение: 1 встречается: 5 раз.В некоторых случаях вместо инструкции if-else можно использовать более краткое иtemplate <class valueType>inline const valueType&min( valueType &vall, valueType &va12 ){if ( vall < va12 )return vall;return va12;выразительное условное выражение.
Например, следующую реализацию функции min():}можно переписать так:195С++ для начинающихtemplate <class valueType>inline const valueType&min( valueType &vall, valueType &va12 ){return ( vall < va12 ) ? vall : va12;}Длинные цепочки инструкций if-else, подобные приведенной ниже, трудны дляif ( ch == 'a' ||ch == 'A' )++aCnt;elseif ( ch == 'e' ||ch == 'E' )++eCnt;elseif ( ch == 'i' ||ch == 'I' )++iCnt;elseif ( ch == 'o' ||ch == '0' )++oCnt;elseif ( ch == 'u' ||ch == 'U' )восприятия и, таким образом, являются потенциальным источником ошибок.++uCnt;В качестве альтернативы таким цепочкам С++ предоставляет инструкцию switch. Этотема следующего раздела.Упражнение 5.3Исправьте ошибки в примерах:196С++ для начинающих(a) if ( ivall != iva12 )ivall = iva12elseivall = iva12 = 0;(b) if ( ivat < minval )minvat = ival;occurs = 1;(c) if ( int ival = get_value())cout << "ival = "<< ival << endl;if ( ! ival )cout << "ival = 0\n";(d) if ( ival = 0 )ival = get_value();(e) if ( iva1 == 0 )else ival = 0;Упражнение 5.4Преобразуйте тип параметра occurs функции min(), сделав его не ссылкой, а простымобъектом.
Запустите программу. Как изменилось ее поведение?5.4. Инструкция switchДлинные цепочки инструкций if-else, наподобие приведенной в конце предыдущегораздела, трудны для восприятия и потому являются потенциальным источником ошибок.Модифицируя такой код, легко сопоставить, например, разные else и if.Альтернативный метод выбора одного их взаимоисключающих условий предлагаетинструкция switch.Для иллюстрации инструкции switch рассмотрим следующую задачу. Нам надоподсчитать, сколько раз встречается каждая из гласных букв в указанном отрывке текста.(Общеизвестно, что буква e – наиболее часто встречающаяся гласная в английскомязыке.) Вот алгоритм программы:1.
Считывать по одному символу из входного потока, пока они не кончатся.2. Сравнить каждый символ с набором гласных.3. Если символ равен одной из гласных, прибавить 1 к ее счетчику.4. Напечатать результат.Написанная программа была запущена, в качестве контрольного текста использовалсяраздел из оригинала данной книги. Результаты подтвердили, что буква e действительносамая частая:aCnt:eCnt:iCnt:oCnt:uCnt:394721461349186197С++ для начинающихИнструкция switch состоит из следующих частей:•ключевого слова switch, за которым в круглых скобках идет выражение,char ch;while ( cm >> ch )являющееся условием:switch( ch )•набора меток case, состоящих из ключевого слова case и константноговыражения, с которым сравнивается условие.