Тело цикла, как и тело функции — это место выполнения инструкций. Значит, мы можем использовать внутри него всё изученное ранее, например — условные конструкции. Представьте себе функцию, которая считает, сколько раз входит буква в предложение. Пример её работы:
count_chars('Fear cuts deeper than swords.', 'e') # 4
# Если вы ничего не нашли, то результат — 0 совпадений
count_chars('Sansa', 'y') # 0
Перед тем как посмотреть её содержимое, попробуйте ответить на вопросы:
def count_chars(string, char):
index = 0
count = 0
while index < len(string):
if string[index] == char:
# Считаем только подходящие символы
count = count + 1
# Счётчик увеличивается в любом случае
index = index + 1
return count
Эта задача является агрегирующей. Несмотря на то, что она считает не все символы, для подсчёта самой суммы все равно приходится анализировать каждый символ. Ключевое отличие этого цикла от рассмотренных в наличии условия внутри тела. Переменная count
увеличивается только в том случае, когда текущий рассматриваемый символ совпадает с ожидаемым. В остальном — это типичная агрегатная функция, которая возвращает количество нужных символов.
Функция my_substr()
, которую вы реализовали в прошлом уроке, содержит множество ошибок. «Но ведь она прошла проверки!». Да, но в этих проверках не было так называемых пограничных случаев. Функция нормально работала с нормальными аргументами, но как она поведёт себя, если передать ей такие варианты длины?
0
.Функция my_substr()
не рассчитана на такие варианты. Можно подумать, что это не проблема: функция работает в нормальных условиях, и просто не нужно передавать ей «плохие» аргументы. В идеальном мире — да, но в реальном мире ваш код будет запускаться в разных ситуациях, с разными комбинациями условий и данных. Нельзя быть уверенным, что аргументы всегда будут корректными, поэтому нужно учитывать все случаи, в рамках здравого смысла.
Ошибки в пограничных случаях — самая частая причина логических ошибок в программах. Программисты всегда забывают что-нибудь учесть. Такие ошибки часто проявляются не сразу и могут долгое время не приводить к видимым проблемам. Программа продолжает работать, но в какой-то момент обнаруживается, что в результатах есть ошибки. Часто причина в динамической типизации Python.
Умение справляться с такими ошибками приходит с опытом, через постоянные косяки в стиле «ой, забыл проверить на пустую строку!».
Давайте представим себе расширенную функцию my_substr()
. Она принимает три аргумента: строку, индекс и длину извлекаемой подстроки. Функция возвращает подстроку указанной длины, начиная с указанного индекса. Примеры вызова:
string = 'If I look back I am lost'
print(my_substr(string, 0, 1)) # => 'I'
print(my_substr(string, 3, 6)) # => 'I look'
Прикинем, что может пойти не так. Какие пограничные случаи стоит учитывать:
В реализации функции каждый пограничный случай будет отдельным куском кода, скорее всего реализованным с помощью if
.
Чтобы написать функцию my_substr()
и защититься от этих случаев, стоит реализовать отдельную функцию, которая будет проверять аргументы на корректность.
Работа с циклами обычно сводится к двум сценариям:
Рассмотрим простой алгоритм проверки простоты числа. Будем делить искомое число x
на все числа из диапазона от двух до x - 1
и смотреть остаток. Если в этом диапазоне не найден делитель, который делит число x
без остатка, значит перед нами простое число. Если задуматься, то можно заметить, что достаточно проверять числа не до x - 1
, а до половины числа. Например, 11 не делится на 2, 3, 4, 5. Но и дальше гарантированно не будет делиться на числа больше своей половины. Значит, можно провести небольшую оптимизацию и проверять деление только до x / 2
.
def is_prime(number):
if number < 2:
return False
divider = 2
while divider <= number / 2:
if number % divider == 0:
return False
divider += 1
return True
print(is_prime(1)) # => False
print(is_prime(2)) # => True
print(is_prime(3)) # => True
print(is_prime(4)) # => False
Алгоритм построен таким образом, что если во время последовательного деления на числа до x / 2
находится хоть одно, которое делит без остатка, то переданный аргумент — не простое число, а значит дальнейшие вычисления не имеют смысла. В этом месте стоит возврат False
.
И только если цикл отработал целиком, можно сделать вывод, что число — простое, так как не было найдено ни одного числа, которое делит число без остатка.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Статья «Ловушки обучения»
Вебинар «Как самостоятельно учиться»
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт