Питон для нормальных. Учебник Москва Базальт спо макс пресс 2018
Скачать 2.54 Mb.
|
3. Сколько человек находится между i-м и k-м в очереди? 4. Сколько нечётных чисел на отрезке [a; b], если a и b — чётные? a и b — нечётные? a — чётное, a — нечётное? 5. Сколько полных часов, минут и секунд содержится в x секундах? Разло- жите имеющееся количество секунд на сумму из x часов + y минут + z секунд. 6. В доме 9 этажей, на каждом этаже одного подъезда по 4 квартиры. В каком подъезде, и на каком этаже находится n-я квартира? 7. Старинными русскими денежными единицами являются: 1 рубль = 100 копеек, 1 гривна = 10 копеек, 1 алтын = 3 копейки, 1 полушка = 0,25 копейки. Имеется A копеек. Разложите имеющуюся сумму в копейках на сумму из x рублей + y гривен + z алтынов + v полушек. 8. Стрелка прибора вращается с постоянной скоростью, совершая w оборо- тов в секунду (не обязательно стрелка прибора, может быть это волчок в игре «Что? Где? Когда?» и т.п.) Угол поворота стрелки в нулевой момент времени примем за 0. Каков будет угол поворота через t секунд? 9. Вы стоите на краю дороги и от вас до ближайшего фонарного столба x метров. Расстояние между столбами y метров. На каком расстоянии от вас находится n-й столб? 10. Та же ситуация, что и в предыдущей задаче. Длина вашего шага z метров. Мимо скольких столбов вы пройдете, сделав n шагов? 11. x — вещественное число. Запишите выражение, позволяющее выделить его дробную часть. 12. x — вещественное число. Запишите выражение, которое округлит его до сотых долей (останется только два знака после запятой). 13. От бревна длиной L отпиливают куски длиной x. Сколько целых полнораз- мерных кусков максимально удастся отпилить? 14. Бревно длиной L распилили в n местах. Какова средняя длина получив- шихся кусков? 15. Резиновое кольцо диаметром d разрезали в n местах. Какова средняя длина получившихся кусков? 2.8. Задания 61 Задание 3 (Строки) Задания выполняйте все по порядку. Свяжите лю- бую переменную со строкой: «Мы обязательно научимся программиро- вать!». Извлеките из неё следующие срезы: 1. выведите третий символ этой строки; 2. выведите предпоследний символ этой строки; 3. выведите первые пять символов этой строки; 4. выведите всю строку, кроме последних двух символов; 5. выведите все символы с чётными индексами (считая, что индексация начи- нается с 0); 6. выведите все символы с нечётными индексами, то есть, начиная с первого символа строки; 7. выведите четыре символа из центра строки; 8. выведите символы с индексами, кратными трём; 9. выведите все символы в обратном порядке; 10. выведите все символы строки через один в обратном порядке, начиная с последнего; 11. удалите второе слово из строки; 12. замените второе слово на строку «никогда не»; 13. добавьте в конец строки «на Python»; 14. поставьте последнее слово первым в строке; 15. выведите длину данной строки. Задание 4 (Логический тип данных. Логические операторы) В каж- дой группе выполнять по одному заданию в зависимости от номера в списке группы: (n − 1)%10 + 1, где n — номер в списке. Вычислить значение логического выражения. Значения переменных x и y вбиваются с клавиатуры. 1. x 2 + x 2 6 4; 2. x 2 − x 2 6 4; 3. x > 0 или y 2 6= 4; 4. x > 0 и y 2 6= 4; 62 Глава 2. Основные типы данных 5. x · y 6= 0 или y > x; 6. x · y 6= 0 и y > x; 7. не x · y < 0 или y > x; 8. не x · y < 0 и y > x; 9. x > 4 или y 2 6= 4; 10. x > 4 и y 2 6= 4. Вычислить значение логического выражения при всех возможных значениях логических величин X, Y и Z (для образца можно взять задачку про Шумахера): 1. не (X или не Y и Z); 2. Y или (X и не Y или Z); 3. не (не X и Y или Z); 4. не (X или не Y и Z) или Z; 5. не (X и не Y или Z) и Y ; 6. не (не X или Y и Z) или X; 7. не (Y или не X и Z) или Z; 8. X и не (не Y или Z) или Y ; 9. не (X или Y и Z) или не X; 10. не (X и Y ) и (не X или не Z). Записать условие (составить логическое выражение), которое является истин- ным, когда: 1. число X делится нацело на 13 и меньше 100; 2. число X больше 10 и меньше 20; 3. каждое из чисел X и Y больше 25; 4. каждое из чисел X и Y нечетное; 5. только одно из чисел X и Y четное; 6. хотя бы одно из чисел X и Y положительно; 7. каждое из чисел X, Y , Z кратно пяти; 8. только одно из чисел X, Y , Z кратно трем; 2.8. Задания 63 9. только одно из чисел X, Y , Z меньше 10; 10. хотя бы одно из чисел X, Y , Z отрицательно. Задание 5 (Условный оператор) Выполнять три задания в зависимо- сти от номера в списке. Необходимо сделать задания № m, № m + 5, № m + 10, где m = (n − 1)%5 + 1, n — номер студента в списке группы в алфавитном порядке. 1. Напишите программу, которая запрашивает значение x, а затем выводит значение следующей функции от x (она называется по латыни «signum», что значит «знак»): y(x) = 1, x > 0, 0, x = 0, −1, x < 0 2. Напишите программу, которая запрашивает значение x, а затем выводит значение следующей функции от x: y(x) = sin 2 (x), x > 0, 0, x = 0, 1 + 2 sin(x 2 ), x < 0 3. Напишите программу, которая запрашивает значение x, а затем выводит значение следующей функции от x: y(x) = cos 2 (x), x > 0, 0, x = 0, 1 − 2 sin(x 2 ), x < 0 4. Запросите у пользователя два числа. Далее: • если первое больше второго, то вычислить их разницу и вывести дан- ные на печать; • если второе число больше первого, то вычислить их сумму и вывести на печать; • если оба числа равны, то вывести это значение на печать. 5. Запросите у пользователя два целых числа m и n. Если целое число m делится нацело на целое число n, то вывести на экран частное от деления, в противном случае вывести сообщение «m на n нацело не делится». 6. Напишите программу для решения квадратного уравнения ax 2 + bx + c = 0. Значения коэффициентов a, b, c вводятся с клавиатуры. Вычисление квадратного корня можно организовать либо путём возведения в степень 64 Глава 2. Основные типы данных 0.5, либо с помощью функции sqrt из математического модуля. Проверяйте значение дискриминанта: если оно меньше нуля, корней нет, если равно нулю, значит, корень 1, если больше нуля — корней два. Для этого можно использовать конструкцию вида if elif else. 7. Напишите программу, решающую кубическое уравнение вида y 3 +px+q = 0 с помощью формулы Кардано. Значения коэффициентов p и q вводятся с клавиатуры. Найдите корни уравнения. Помните, что Python может рабо- тать с комплексными числами, но модуль math использовать для их возве- дения в степень нельзя. Будьте внимательны с кубическим корнем: кубиче- ский корень от отрицательного числа превращается в комплексное число. 8. Напишите программу, которая запрашивает у пользователя его возраст (це- лое число лет) и в зависимости от значения введённого числа выводит: • от 0 до 7 — «Вам в детский сад»; • от 7 до 18 — «Вам в школу»; • от 18 до 25 — «Вам в профессиональное учебное заведение»; • от 25 до 60 — «Вам на работу»; • от 60 до 120 — «Вам предоставляется выбор»; • меньше 0 и больше 120 — пять раз подряд: «Ошибка! Это программа для людей!». 9. Напишите программу, которая поможет вам оптимизировать путешествие на автомобиле. Пусть программа запрашивает у пользователя следующие данные: • Сколько километров хотите проехать на автомобиле? • Сколько литров топлива расходует автомобиль на 100 километров? • Сколько литров топлива в вашем баке? Далее в зависимости от введённых значений программа должна выдать вердикт: проедете вы желаемое расстояние или нет; 10. Пользователь вводит три действительных числа: длины сторон треуголь- ника. Программа должна сообщить пользователю: • является ли треугольник равносторонним; • является ли треугольник равнобедренным; • является ли треугольник разносторонним; • является ли треугольник прямоугольным; • существует ли вообще такой треугольник (такого треугольника не мо- жет быть, если длина хотя бы одной стороны больше или равна сумме длин двух других). 2.8. Задания 65 11. Известен вес боксёра-любителя. Он таков, что боксёр может быть отнесен к одной из трех весовых категорий: • легкий вес — до 60 кг; • первый полусредний вес — до 64 кг; • полусредний вес — до 69 кг; Определить, в какой категории будет выступать данный боксер. 12. В чемпионате по футболу команде за выигрыш дается 3 очка, за проиг- рыш — 0, за ничью — 1. Известно количество очков, полученных командой за игру. Определить словесный результат игры (выигрыш, проигрыш или ничья). 13. Составить программу, которая в зависимости от порядкового номера дня недели (от 1 до 7) выводит на экран его название (понедельник, вторник, ..., воскресенье). 14. Составить программу, которая в зависимости от порядкового номера ме- сяца (1, 2, ..., 12) выводит на экран его название (январь, февраль, ..., декабрь). 15. Составить программу, которая в зависимости от порядкового номера меся- ца (1, 2, ..., 12) выводит на экран время года, к которому относится этот месяц. Задание 6 (Списки. Кортежи. Словари) Задания выполнять все по по- рядку. 1. Списки a) Создайте два списка в диапазоне (0, 100) с шагом 10. Присвойте неко- торым переменным значения этих списков. b) Извлеките из первого списка второй элемент. c) Измените во втором списке последний объект на число «200». Выве- дите список на экран. d) Соедините оба списка в один, присвоив результат новой переменной. Выведите получившийся список на экран. e) Возьмите срез из соединённого списка так, чтобы туда попали неко- торые части обоих первых списков. Срез свяжите с очередной новой переменной. Выведите значение этой переменной. f) Добавьте в список-срез два новых элемента и снова выведите его. g) С помощью функций min() и max() найдите и выведите элементы объ- единённого списка с максимальным и минимальным значением. 66 Глава 2. Основные типы данных 2. Кортежи a) Создайте два кортежа: один из чисел в диапазоне (1, количество уче- ников в группе) с шагом 1, второй — из фамилий учеников вашей группы. Пусть они соответствуют друг другу; b) Посмотрите, какая фамилия у студента с номером 5. c) А теперь посмотрите, что записано во второй кортеж под номером 5. d) Объедините два кортежа в один, присвоив результат новой перемен- ной. Выведите получившийся список на экран. e) Возьмите срез из соединенного кортежа так, чтобы туда попали неко- торые части обоих первых кортежей. Срез свяжите с очередной новой переменной. Выведите значение этой переменной. 3. Словари a) Создайте словарь, связав его с переменной School, и наполните его данными, которые бы отражали количество учащихся в пяти разных классах (например, 1а, 1б, 2в и т. д.); выведите содержимое словаря на экран. b) Узнайте сколько человек в каком-нибудь классе. Класс запрашивается у пользователя с клавиатуры, если такого запрашиваемого класса в школе нет, то выдаётся сообщение: «Такого класса на существует». c) В школе произошли изменения, внесите их в словарь: в трёх классах изменилось количество учащихся; результат выведите на экран. d) В школе появилось два новых класса, новый словарь выведите на экран. e) В школе расформировали один из классов, выведите содержимое но- вого словаря на экран. Глава 3 Циклы Циклы — это инструкции, выполняющие одну и ту же последовательность действий многократно. В реальной жизни мы довольно часто сталкиваемся с циклами. Например, ходьба человека — вполне циклическое явление: шаг левой, шаг правой, сно- ва левой-правой и т. д., пока не будет достигнута определенная цель (например, университет или кафе). В компьютерных программах наряду с инструкциями ветвления (т.е. выбором пути действия, конструкция if-else) также существуют инструкции циклов (повторения действия). Если бы инструкций цикла не суще- ствовало, пришлось вставлять в программу один и тот же код подряд столько раз, сколько нужно выполнить одинаковую последовательность действий. 3.1 Цикл с условием (while) Универсальным организатором цикла в языке программирования Python (как и во многих других языках) является цикл с условием (конструкция while). Сло- во «while» с английского языка переводится как «пока» (пока логическое вы- ражение возвращает истину, выполнять определенные операции). Конструкция while на языке Python может выглядеть следующим образом 1 : a = начальное значение w h i l e a оператор сравнения b : действия изменение a действия Эта схема сильно неполная, так как логическое выражение в заголовке цикла может быть более сложным, а изменяться может переменная (или выражение) b. 1 В действительности, такое представление является частным и вместо a оператор сравнения b может стоять любое логическое выражение и даже нелогическое выражение, ко- торое может быть интерпретировано как логическое путём неявных преобразований типов. 68 Глава 3. Циклы Может возникнуть вопрос: «Зачем изменять a или b?». Когда выполнение программного кода доходит до цикла while, выполняется логическое выраже- ние в заголовке, и, если было получено True, выполняются вложенные выраже- ния. После поток выполнения программы снова возвращается в заголовок цикла while , и снова проверяется условие. Внимание! Если условие никогда не будет ложным, то не будет причин для остановки цикла, и программа зациклится. Простейший способ создать такую ситуацию: w h i l e True : p r i n t ( ’У попа была собака, он её любил.’) p r i n t ( ’Она съела кусок мяса - он её убил.’) p r i n t ( ’Вырыл ямку, закопал и на камне написал:’) Чтобы такого не произошло в обычной программе, необходимо предусмот- реть возможность выхода из цикла — ложность выражения в заголовке. Таким образом, изменяя значение переменной в теле цикла, можно довести логическое выражение до ложности. Эту изменяемую переменную, которая используется в заголовке цикла while, обычно называют счётчиком. Как и всякой переменной, ей можно давать произвольные имена, однако очень часто используются буквы i и j. Пример использования цикла while: вывод первых n чисел Фибоначчи. Ряд Фибоначчи — ряд чисел, в котором каждое последующее число равно сумме двух предыдущих: 0, 1, 1, 2, 3, 5, 8, 13 и т. д. Выведем первые 10 чисел: fib1 = 0 fib2 = 1 p r i n t ( fib1 ) p r i n t ( fib2 ) n = 10 i = 2 summa = 0 w h i l e i <= n : summa = fib1 + fib2 p r i n t ( summa ) fib1 = fib2 fib2 = summa i = i + 1 Как работает эта программа? Вводятся две переменные (fib1 и fib2), ко- торым присваиваются начальные значения. Присваиваются начальные значения переменным n и summa, а также счетчику i. Внутри цикла переменной summa при- сваивается сумма двух предыдущих членов ряда, и ее же значение выводится на экран. Далее изменяются значения fib1 и fib2 (первому присваивается второе, второму — сумма), а также увеличивается значение счетчика. Задачка из жизни. В багажник автомобиля грузят овощи и фрукты с да- чи: картофель, капусту, морковь, яблоки, груши и др. Объем багажника равен 3.1. Цикл с условием (while) 69 350 л. Продукты кладут последовательно, объём каждого груза известен в лит- рах. Нужно сказать в какой момент (назвать номер груза) багажник перепол- нится. Программа выглядит следующим образом: s = 0 n = 0 w h i l e s < 350: x = i n t ( i n p u t ()) s = s + x n = n + 1 p r i n t ( n ) Здесь переменная s хранит суммарный объём уже накопленных грузов, в пере- менную x считывается объём очередного груза, а n считает номер груза. В обоих примерах был применен важный приём накопления суммы. Данный алгоритмический приём используется, когда надо просуммировать большое ко- личество чисел. Для этого переменной, в которую будет записываться сумма, в начале присваивается нулевое значение, затем делается цикл, где на каждом шаге к этой переменной добавляется очередное число. Очень важная, фундаментальная идея, использованная в данном приёме, со- стоит в том, что результат выполнения каждого шага цикла зависит от значения переменной, вычисленного на предыдущем. Таким образом, вместо тривиального повторения одного и того же мы на каждом шаге получаем новый результат. В приведенном примере очередное число добавляется к значению перемен- ной s, полученному на предыдущем шаге. А к чему добавляется очередное чис- ло на самом первом? Чтобы было к чему добавлять, перед циклом обязательно должна присутствовать инициализация (присваивание начального значения) пе- ременной, в которой накапливается сумма. Чаще всего требуется присвоить ей начальное значение 0. Программистский анекдот в тему. Буратино подарили три яблока. Два он съел. Сколько яблок осталось у Буратино? Ответ «одно» — неправильный. В действительности, неизвестно, сколько осталось, так как не сказано, сколько яб- лок было у него до того, как ему подарили три новых. Мораль: не забывайте обнулять (и вообще инициализировать) переменные! Аналогично накоплению суммы можно в отдельной переменной накапливать произведение. Переменной, в которой производится накопление, присваивается начальное значение 1. Для примера вычислим факториал некоторого числа. Фак- ториалом целого числа n называется произведение всех целых чисел от 1 до n. Обозначается n!, то есть n! = 1 · 2 · 3 · . . . · n. Вычисляющая факториал программа выглядит так: n = i n t ( i n p u t ( ’Сколько факториалов будем суммировать? ’ )) i = 2 p = 1 w h i l e i <= n : 70 Глава 3. Циклы p = p * i i = i + 1 p r i n t ( p ) 3.2 Цикл обхода последовательности (for) Цикл while не единственный способ организации повторения группы выра- жений. Также широко применяется цикл for, который представляет собой цикл обхода заданного множества элементов (символов строки, объектов списка или словаря) и выполнения в своем теле различных операций над ними 2 Как правило, циклы for используются либо для повторения какой-либо по- следовательности действий заданное число раз, либо для изменения значения переменной в цикле от некоторого начального значения до некоторого конечно- го. Для повторения цикла некоторое заданное число раз n можно использовать цикл for вместе с функцией range: f o r i i n r a n g e ( n ): Тело цикла В качестве n может использоваться числовая константа, переменная или произ- вольное арифметическое выражение (например, 2**10). Если значение n равно нулю или отрицательное, то тело цикла не выполнится ни разу. Если задать цикл таким образом: f o r i i n r a n g e (a , b ): Тело цикла то индексная переменная i будет принимать значения от a до b - 1 включитель- но, то есть первый параметр функции range, вызываемой с двумя параметрами, задает начальное значение индексной переменной, а второй параметр — значение, которое индексная переменная принимать не будет. Например, для того, чтобы просуммировать значения чисел от 1 до n, можно воспользоваться следующей программой: summa = 0 f o r i i n r a n g e (1 , n +1): summa = summa + i В этом примере переменная i принимает значения 1, 2, . . . , n, и значение переменной summa последовательно увеличивается на указанные значения. Здесь опять видим прием накопления суммы. 2 В большинстве языков программирования под циклом for принято понимать цикл со счёт- чиков. Такого цикла в Python нет, хотя существующий for может исполнять его функции. Цикл for языка Python фактически представляет собою цикл foreach — для каждого. Такой цикл существует во многих языках программирования, созданных в последние два десятилетия, на- пример, в D. 3.2. Цикл обхода последовательности (for) 71 Наконец, чтобы организовать цикл, в котором индексная переменная будет уменьшаться (в Pascal цикл с downto, цикл с отрицательным приращением), необ- ходимо использовать функцию range с тремя параметрами. Первый параметр задает начальное значение индексной переменной, второй параметр — значение, до которого будет изменяться индексная переменная (не включая его!), а третий параметр — величину изменения индексной переменной. Например, сделать цикл по всем нечетным числам от 1 до 99 можно при помощи функции range(1, 100, 2) , а сделать цикл по всем числам от 100 до 1 можно при помощи range(100, 0, -1) Более формально, цикл for i in range(a, b, d) при d > 0 задаёт значения индексной переменной i = a, i = a + d, i = a + 2 * d и так для всех значений, для которых i < b. Если же d < 0, то переменная цикла принимает все значения i > b Но в языке программирования Python цикл for имеет зачастую несколь- ко иное применение. Например, список в Python относится к итерируемым объектам. Это значит, что его элементы можно обойти циклом for, причём переменная-счётчик будет на каждом шаге принимать значение очередного эле- мента цикла: mylist = [12 , 17.9 , True , -8 , False ] f o r j i n mylist : p r i n t ( j ) Программа выведет все элементы списка mylist в столбик: 12 17.9 True -8 False Приведённый способ можно назвать обходом по значению, поскольку автома- тически создаваемая переменная j на каждом шаге принимает значение очеред- ного элемента списка. Есть ещё один способ обойти список — по индексам, когда такая же переменная будет принимать номер очередного элемента: mylist = [12 , 17.9 , True , -8 , False ] f o r j i n r a n g e (0 , l e n ( mylist ) , 1): p r i n t ( j ) Вывод будет совсем другой: 0 1 2 3 4 72 Глава 3. Циклы Если написать: mylist = [12 , 17.9 , True , -8 , False ] f o r j i n r a n g e (0 , l e n ( mylist ) , 1): p r i n t ( mylist [ j ]) то вывод будет такой же, как в первом примере. На самом деле, механизм обоих подходов один и тот же, потому что во вто- ром варианте фактически неявно создаётся новая последовательность range(0, len(mylist), 1) , содержащая номера всех элементов списка mylist — диапазон от нуля до длины mylist с шагом 1, и этот новая последовательность обходится по значению. Напишем с помощью цикла for вывод ряда Фибоначчи: fib1 = 0 fib2 = 1 n = 10 summa = 0 f o r i i n r a n g e ( n ): summa = fib1 + fib2 p r i n t ( summa ) fib1 = fib2 fib2 = summa В результате будет выведено следующее: 1 2 3 5 8 13 21 34 55 89 С помощью цикла for можно перебирать строки, если не пытаться их при этом изменять: str1 = ’Привет’ f o r i i n str1 : p r i n t (i , end = ’Ђ ’) Будет выведено: П р и в е т 3.3. Некоторые основные алгоритмические приёмы 73 Здесь можно видеть, что у функции print есть параметр end. По умолчанию end = ’\n’ — неотображаемому символу новой строки. В предыдущем примере параметру end был присвоен символ пробел. Цикл for используется и для работы со словарями: dic = { ’ cat ’: ’кошка’ , ’ dog ’: ’пёс’ , ’ bird ’: ’птица’ , ’ mouse ’: ’мышь’} f o r i i n dic : dic [ i ] = dic [ i ] + ’ _ru ’ p r i n t ( dic ) Вывод программы: {’bird’: ’птица_ru’, ’cat’: ’кошка_ru’, ’mouse’: ’мышь_ru’, ’dog’: ’пёс_ru’} На практике часто не важно, каким образом вы решите задачу. Искать сразу оптимальное решение не следует, достаточно найти просто правильное (а их мо- жет быть множество). Как писал Дональд Кнут в своём фундаментальном труде «Искусство программирования», «преждевременная оптимизация — корень мно- гих зол». 3.3 Некоторые основные алгоритмические приёмы 3.3.1 Приёмы накопления суммы и произведения. Их комбинация В разделе про цикл while рассматривались примеры с накоплением суммы и произведения. Эти же приёмы можно и нужно применять при работе с циклом for . Так же их можно комбинировать. Рассмотрим следующий пример: необхо- димо вычислить значение выражения 1! + 2! + · · · + n! Решение в лоб состоит в том, чтобы в теле цикла, осуществляющего сумми- рование, производить вычисление факториала: n = i n t ( i n p u t ( ’Сколько факториалов будем суммировать? ’ )) s = 0 f o r i i n r a n g e (1 , n +1): # Вычисление факториала от i : p = 1 f o r k i n r a n g e (1 , i +1): p = p * k ; # Добавление вычисленного факториала к сумме: s = s + p p r i n t ( s ) Циклы позволяют повторять выполнение любого набора операторов. В част- ности, можно повторять много раз выполнение другого цикла. Такие циклы на- зываются вложенными. В приведённом выше примере один цикл for вложен в другой цикл for. 74 Глава 3. Циклы Типичная ошибка, когда в качестве счётчиков вложенных циклов (i и k в приведённом примере) используется одна и та же переменная. То есть, нельзя в каждом из циклов использовать одну переменную i. Ваша программа запустит- ся, но делать будет вовсе не то, что вы от неё ждёте. В приведённом примере, если допустить ошибку, заменив переменную k на i, внешний цикл выполнится всего 1 раз вместо 4-х. Возможна также ситуация, когда такая ошибка приведет к зацикливанию: внешний цикл будет выполняться бесконечно долго — программа зависнет. Заметим, что при вычислении факториала на каждом шаге получается фак- ториал все большего целого числа. Эти «промежуточные» результаты однократ- ного вычисления факториала и можно суммировать: n = i n t ( i n p u t ( ’Сколько факториалов суммировать? ’ )) s = 0 p = 1 f o r i i n r a n g e (1 , n +1): p = p * i s = s + p p r i n t ( s ) Стоит отметить, что в основе рассмотренных ранее алгоритмических приё- мов накопления суммы и произведения лежит фундаментальная идея о том, что результат вычислений на каждом шаге цикла должен зависеть от результата вы- числений на предыдущем шаге. Обобщенным математическим выражением этой идеи являются рекуррентные соотношения. В наших примерах в качестве рекуррентных соотношений выступали, напри- мер, формулы p = p * i и s = s + p. Причём, последнее выражение (s = s + p ) является сложною рекурсией, когда значение s зависит не только от своего прошлого значения, но и от значения p на прошлом шаге. Для лучшего понимания решим задачу: пусть дано рекуррентное соотноше- ние x n+1 = 1 − λx 2 n . В нелинейной динамике это соотношение называют логисти- ческим отображением. Оно, например, может использоваться для приближённо- го описания изменения численности популяций некоторых животных во времени. Пусть начальное значение x 0 = 1. Параметр λ = 0.75. Необходимо найти x 5 x = 1 f o r i i n r a n g e (1 , 6): x = 1 - 0.75* x **2 p r i n t ( x ) Вывод программы: 0 . 3 5 9 8 9 0 1 8 6 9 3 6 2 9 4 0 4 Однократное вычисление следующих значений по предыдущим посредством рекуррентных соотношений называется итерацией. А процесс вычислений с по- мощью рекуррентных соотношений — итерированием. 3.3. Некоторые основные алгоритмические приёмы 75 Задание 7 Придумайте рекуррентное соотношение, задающее следую- щие числовые последовательности: a) 1, 2, 3, 4, . . . b) 0, 5, 10, 15, . . . c) 1, 1, 1, 1, . . . d) 1, −1, 1, −1, . . . e) 1, −2, 3, −4, 5, −6 . . . f) 2, 4, 8, 16, . . . g) 2, 4, 16, 256, . . . h) 0, 1, 2, 3, 0, 1, 2, 3, 0, . . . i) 1!, 3!, 5!, 7!, . . . Важно!!! Если в написанной вами формуле вам встречается значок суммы P N −1 i=0 x i , то вы сразу должны представлять себе цикл for с накоплением суммы внутри: x = 0 f o r i i n r a n g e (0 , N ): x = x + i Аналогично, если в задании вам встречается значок произведения Q N −1 i=0 x i , то вы сразу должны представлять себе цикл for с накоплением произведения внутри: x = 1 f o r i i n r a n g e (0 , N ): x = x * i 3.3.2 Счётчик событий Часто требуется подсчитать, сколько раз во время вычислений наступает то или иное событие (выполняется то или иное условие). Для этого вводится вспо- могательная переменная, которой в начале присваивается нулевое значение, а после каждого наступления события она увеличивается на единицу. Пример задачи 10 (Счётчик событий) Пользователь вводит 10 чисел. Определите, сколько из них являются одновременно чётными и поло- жительными. 76 Глава 3. Циклы Решение задачи 10 Решение можно записать следующим образом: Counter = 0 # Обнуляем переменную-счётчик f o r i i n r a n g e (0 , 10): x = i n t ( i n p u t ( ’Введите число: ’ )) i f ( x %2 == 0) a n d ( x > 0): Counter = Counter + 1 p r i n t ( Counter ) Вывод программы: Введите число: 2 Введите число: 3 Введите число: 4 Введите число: -2 Введите число: -4 Введите число: 3 Введите число: 5 Введите число: 7 Введите число: 6 Введите число: 3 3 3.3.3 Досрочное завершение цикла Отметим два простых оператора break и continue, с помощью которых мож- но управлять ходом выполнения цикла. Оператор break прерывает выполне- ние цикла, управление передается операторам, следующим за оператором цикла. Оператор continue прерывает выполнение очередного шага цикла и возвращает управление в начало цикла, начиная следующий шаг. f o r n i n r a n g e (10): i f n %2 == 0: o n t i n u e i f n == 7: b r e a k p r i n t ( n ) Данная программа будет печатать только нечётные числа из-за срабатывания continue . Цикл прекратит выполняться, когда n станет равно 7. В итоге вывод программы таков: 1 3 5 3.3. Некоторые основные алгоритмические приёмы 77 3.3.4 Поиск первого вхождения Ранее мы подсчитывали количество положительных чётных чисел в последо- вательности ввода. Зачастую нужен не подсчёт, а только проверка, произошло ли за время вычислений некоторое событие. Например, необходимо проверить, содержится ли в некоторой последовательности хотя бы одно отрицательное число. Для того, чтобы утверждать, что отрицательных чисел в последователь- ности нет, необходимо просмотреть её всю. Если же такое число в ней есть, до- статочно добраться до него, после чего цикл можно закончить. Получается цикл for с проверкой и оператором break внутри. seq = (12 , 54 , 0 , -7 , 22 , -11 , 54 , 0 , -7) f o r x i n seq : i f x < 0: p r i n t ( x ) b r e a k В этом коде пока нет места для действий на случай, если отрицательное число не найдено. В самом деле, и после break, и после «естественного» завершения цикла программа продолжит работу с одной и той же строки. В Python на этот счёт предусмотрена конструкция else, относящаяся к циклу. Работает она так, как и ожидается: только если цикл завершился «естественным путём» — пото- му что проверка условия в while оказалась ложной или последовательность в for закончилась. Если же выход из цикла произошёл по break, блок операторов внутри else не выполняется. Так что для того, чтобы вывести какое-нибудь со- общение, если отрицательных чисел в последовательности нет, соответствующий print() надо добавить в такую конструкцию. seq = (12 , 54 , 0 , 7 , 22 , 11 , 54 , 0 , 7) f o r x i n seq : i f x < 0: p r i n t ( x ) b r e a k e l s e : p r i n t ( "Отрицательных чисел нет" ) 3.3.5 Обработка исключений Исключения (exceptions) — ещё один тип данных в Python. Часто в работе программы возникают ошибки, препятствующие её дальнейшему выполнению. Вот простой пример такой ошибки: >>> 10/0 T r a c e b a c k ( most recent call last ): File " < p y s h e l l #0 > " , line 1 , i n < module > 10/0 Z e r o D i v i s i o n E r r o r : d i v i s i o n by zero 78 Глава 3. Циклы В данном случае интерпретатор сообщил нам об ошибке ZeroDivisionError, то есть о делении на ноль. Также возможны и другие исключения, например, несовпадающие типы: >>> 1 + ’a ’ T r a c e b a c k ( most recent call last ): File " < p y s h e l l #1 > " , line 1 , i n < module > 1 + ’a ’ T y p e E r r o r : u n s u p p o r t e d o p e r a n d t y p e ( s ) f o r +: ’ int ’ a n d ’ str ’ Во всех таких случаях интерпретатор прерывает работу программы, посколь- ку либо не может понять очередную инструкцию, либо предполагает, что полу- ченное в её результате значение (например, при делении на ноль или взятии логарифма отрицательного числа) недопустимо. Это считается правильным, по- скольку указывает программисту на наличие ошибки. Однако иногда в програм- ме могут возникать ошибки, которые невозможно быстро поправить, а работу программы останавливать нельзя. В таком случае принято говорить об исключе- нии. Такие исключения можно обрабатывать, для чего используется конструк- ция try–except. Пример применения этой конструкции: a = i n t ( i n p u t ( ’Введите делимое = ’ )) b = i n t ( i n p u t ( ’Введите делитель = ’ )) t r y : p r i n t ( a / b ) e x e p t Z e r o D i v i s i o n E r r o r : p r i n t ( ’Деление на ноль’) Надо понимать, что обработка исключений — это крайняя мера, которая ис- пользуется, либо если иначе починить программу без существенного переписы- вания быстро нельзя, либо если программа зависит от сторонних модулей, кото- рые не могут быть исправлены, но способны вызвать ошибку. Злоупотребление конструкцией try–except быстро приводит программу в неработоспособное со- стояние, поскольку эта конструкция в действительности ничего не исправляет, а просто помогает игнорировать проблему в данном месте. В большинстве случаев вместо try–except достаточно добавить просто проверку условия. 3.4 Отладка программы В большинстве случаев многие даже несложные программы, будучи напи- саны, работают не так, как предполагал автор. Возможно, вы уже убедились в этом при написании простеньких программ из предыдущей главы. Как минимум, у половины из вас появлялись синтаксические ошибки (забыли поставить скобоч- ку или кавычку). В этом разделе после изучения таких сравнительно сложных конструкций, как циклы, количество ваших ошибок резко увеличится. Но рас- страиваться не стоит. Нужно всегда помнить, что процесс написания программы состоит из двух этапов: кодирование (написание кода программы, занимает менее трети времени) и отладки (занимает более двух третей времени). 3.4. Отладка программы 79 Рис. 3.1. Пример ошибки, выявляемой препроцессором. Все ошибки можно условно разделить на следующие три категории, которые разберём на примере программы, считающей сумму квадратов целых чисел от 0 до N . 3.4.1 Ошибки, выявляемые препроцессором В интерпретатор Python встроена специальная программа — препроцессор. У препроцессора несколько функций, в частности, он переводит текст програм- мы в специальный байт-код, понятный для интерпретатора. В процессе перево- да текста в байт-код препроцессор вынужден анализировать синтаксис вашей программы, для чего используется синтаксический анализатор, проверяющий ваш текст с целью понять, похож ли он на текст программы на Python по ря- ду формальных признаков. Если препроцессор не может понять смысл тех или иных символов в вашем тексте, он чаще всего указывает вам на ошибку типа (SyntaxError). При синтаксической ошибке возникает диалоговое окно, которое предотвращает запуск интерпретатора (рис. 3.1), так как нет смысла запускать то, что непохоже на программу. Самый важный вид синтаксической ошибки с точки зрения препроцессора — это ошибка расстановки отступов, поскольку она нарушает всю структуру программы. Если вы попытаетесь запустить на исполнение вот такой код: N = i n t ( i n p u t ( ’Введите N : ’ )) s = 0 f o r i i n r a n g e ( N ): s = s + i **2 i f s % 2 == 0: p r i n t ( s ) то ничего не выйдет: вы получите сообщение unexpected indent — неожидан- ный отступ, и пробел перед ключевым словом for будет подсвечен красным. Исправить такую ошибку совсем несложно: нужно просто нормально расставить отступы в соответствии с логикою программы. Ещё одна популярная ошибка на примере того же кода: 80 Глава 3. Циклы N = i n t ( i n p u t ( ’Введите N : ’ )) s = 0 f o r i i n r a n g e ( N ): s = s + i **2 i f s % 2 == 0: p r i n t ( s ) Интерпретатор выдаст: expected an indented block: нужен отступ для тех команд, которые лежат внутри цикла for и условного оператора if. Бывает, что в результате опечаток возникают недопустимые с точки зрения интерпретатора выражения. Например, можно допустить следующую ошибку: s = s + 2 i С точки зрения правил Python выражение 2i никогда не может возникнуть: имя переменной не может начинаться с цифры, а для интепретации 2 и i как разных сущностей между ними должен быть знак какой-нибудь арифметической или логической операции (чаще всего забывают знак умножения *, поскольку в математических выражениях он обычно опускается). Чуть сложнее разобраться с другим подвидом синтаксических ошибок, вы- званных неверною расстановкою скобок: N = i n t ( i n p u t ( ’Введите N : ’) s = 0 f o r i i n r a n g e ( N ): s = s + i **2 p r i n t ( s ) Такой пример вызовет ошибку invalid syntax , причём укажет на символ s в начале второй строки, что может сбить вас с толку. На самом деле проблема в несоответствии числа открывающихся и закрывающихся скобок в предыду- щей строке. Интерпретатор в поисках второй закрывающейся скобки дошёл до строки, следующей за тою, где совершена ошибка, и, поняв, что искать дальше бессмысленно (на новой строке по правилам её уже не может быть), выдал ошиб- ку. При неверном числе скобок интерпретатор всегда выдаёт ошибку в начале следующей строки. 3.4.2 Ошибки, выявляемые интерпретатором Если вы успешно справились с синтаксисом, другие ошибки за вас может выявить интерпретатор во время исполнения программы. Интерпретатор даже напишет, что это за ошибка и в какой она строчке кода (рис. 3.2). Ошибки, выявляемые интерпретатором, также называются ошибками време- ни исполнения. Самые распространённые из них — ошибки пространства имён. Это такие ошибки, когда имя функции, метода или введённой вами же перемен- ной написано неверно. Кроме них часто возникают ошибки неверной типизации и 3.4. Отладка программы 81 Рис. 3.2. Пример ошибки, выявляемой интерпретатором во время исполнения. ошибки, связанные с недопустимыми операциями с памятью компьютера. Далее основные ошибки разобраны более подробно: 1. NameError — ошибка в имени. Вот пример неправильно написанного имени стандартной функции range: N = i n t ( i n p u t ( ’Введите N : ’ )) s = 0 f o r i i n rnage ( N ): s = s + i **2 При попытке выполнить этот код получится следующее: Traceback (most recent call last): File "/home/paelius/test_error.py", line 3, in for i in rnage(N): NameError: name ’rnage’ is not defined Как видим, интерпретатор, дойдя до строчки с ошибкою, указал нам, что имя rnage ему неизвестно ( NameError: name ’rnage’ is not defined ). Найти и исправить такие ошибки обычно довольно просто, в том числе, благодаря тому, что все встроенные функции (range, len, sorted, sum, int и другие) выделяются цветом (в IDLE это фиолетовый). Поэтому вы можете контро- лировать себя уже на этапе написания кода: если range не подсветилось, значит, вы написали что-то неверно. Аналогично другим — жёлтым — цве- том выделяются встроенные операторы и их части: in, for, while, if, else, from , import, as, with, break, continue, а также встроенные значения: True, False и None. 2. AttributeError — ошибочный атрибут. NameError — не единственная лек- сическая ошибка. Перепишем задачу так, что сначала положим все квад- раты чисел в список, а затем воспользуемся стандартной функцией sum: l = [] f o r i i n r a n g e ( N ): 82 Глава 3. Циклы l . apend ( i **2) p r i n t ( s u m ( l )) В этой программе есть одна трудно уловимая ошибка: в методе append про- пущена одна буква p. В результате мы получим: AttributeError: Traceback (most recent call last): File "/home/paelius/test_error.py", line 4, in l.apend(i**2) AttributeError: ’list’ object has no attribute ’apend’ Интерпретатор указывает нам, что объект данного типа не имеет атрибута (метода или поля) apend. Поскольку методы даже стандартных объектов таких, как список, никак не подсвечиваются, обнаружить эту ошибку за- ранее сложно. Плюс в том, что исправление подобной ошибки не составит труда. Есть, однако, один способ снизить вероятность их появления: для длинных методов, имя которых вы плохо помните, лучше пользоваться ав- тодополнением. 3. TypeError — ошибка типов. Всегда следует помнить, что в третьей версии Python функция input() возвращает строковую переменную. Если попро- бовать написать что-то подобное: a = i n p u t () b = i n p u t () p r i n t ( a / b ) то получим ошибку: Traceback (most recent call last): File "E:/Python/1.py", line 3, in print(a/b) TypeError: unsupported operand type(s) for /: ’str’ and ’str’ Для выявления подобных ошибок полезно выводить на экран тип перемен- ной командою print(type(a)). 4. ValueError — ошибка значения, являющаяся ещё одним видом ошибок, связанных с типами данных. Она возникает, например, при попытке из- влечь корень из отрицательного числа. Причём интересно, что ошибка бу- дет выдана только при использовании функции sqrt из модуля math, а при возведении в степень стандартным образом с помощью оператора ** число будет просто конвертировано в комплексное: 3.4. Отладка программы 83 >>> ( -3)**(1/2) ( 1 . 0 6 0 5 7 5 2 3 8 7 2 4 9 0 6 8 e - 1 6 + 1 . 7 3 2 0 5 0 8 0 7 5 6 8 8 7 7 2 j ) >>> i m p o r t math >>> math . sqrt ( -3) T r a c e b a c k ( most recent call last ): File " < pyshell #2 > " , line 1 , i n < module > math . sqrt ( -3) V a l u e E r r o r : math domain error 5. IndexError — ошибка индекса. Появляется при обращении к несуществу- ющему элементу строки или списка: >>> L = l i s t ( r a n g e (10)) >>> L [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9] >>> L [10] T r a c e b a c k ( most recent call last ): File " < pyshell #5 > " , line 1 , i n < module > L [10] I n d e x E r r o r : l i s t index out of r a n g e 6. OverflowError — ошибка переполнения. Возникает, когда в результате вы- числений получается слишком большое действительное число: p = 1.5 f o r i i n r a n g e (2 , 100): p = p ** i p r i n t ( p ) В результате будет выдана ошибка: T r a c e b a c k ( most recent call last ): File " E :/ Python /1. py " , line 3 , i n < module > p = p ** i O v e r f l o w E r r o r : (34 , ’ Result Ђ too Ђ large ’) 3.4.3 Ошибки, выявляемые разработчиком Их ещё можно назвать логическими. Это такие ошибки, когда ваша програм- ма работает, но выдаёт что-то не то. Это наиболее сложный тип ошибок, потому что их нужно не только устранять, но и выявлять самостоятельно, а для этого необходимо думать. Вернёмся к нашей программе расчёта суммы квадратов последовательных целых чисел: 84 Глава 3. Циклы Рис. 3.3. Пример логической ошибки, которая может быть выявлена только раз- работчиком. N = i n t ( i n p u t ( ’Введите N : ’ )) s = 0 f o r i i n r a n g e ( N ): s = s + i *2 p r i n t ( s ) В окне интерпретатора вы увидите: Введите N : 5 20 Казалось бы, всё неплохо: программа работает и выдаёт что-то разумное. Но не спешите радоваться, ведь на самом деле 0 2 + 1 2 + 2 2 + 3 2 + 4 2 = 30, а вовсе не |