Ранее была задача вычисления числа сочетаний из n элементов по k, для чего необходимо вычисление факториалов трех величин: n, k и n-k. Для этого можно сделать три цикла, что приводит к увеличению размера программы за счет трехкратного повторения похожего кода. Вместо этого лучше сделать одну функцию, вычисляющую факториал любого данного числа n и трижды использовать эту функцию в своей программе. Соответствующая функция может выглядеть так:
def factorial(n): f = 1 for i in range(2, n + 1): f *= i return f
Этот текст должен идти в начале программы, вернее, до того места,
где мы захотим воспользоваться функцией factorial
.
Первая строчка этого примера является описанием нашей функции.
factorial
— идентификатор, то есть имя нашей функции.
После идентификатора в круглых скобках идет список параметров,
которые получает наша функция. Список состоит из перечисленных через запятую
идентификаторов параметров. В нашем случае список состоит из одной величины n
.
В конце строки ставится двоеточие.
Далее идет тело функции, оформленное в виде блока, то есть с отступом.
Внутри функции вычисляется значение факториала числа n
и оно сохраняется в переменной f
. Функция завершается инструкцией return f
,
которая завершает работу функции и возвращает значение переменной f
.
Инструкция return
может встречаться в произвольном месте функции, ее исполнение
завершает работу функции и возвращает указанное значение в место вызова.
Если функция не возвращает значения, то инструкция return
используется без возвращаемого значения, также в функциях, не возвращающих значения,
инструкция return
может отсутствовать.
Теперь мы можем использовать нашу функцию несколько раз.
В этом примере мы трижды вызываем функцию factorial
для вычисления трех факториалов:
factorial(n)
, factorial(k)
, factorial(n-k)
.
n = int(input()) k = int(input()) print(factorial(n) // (factorial(k) * factorial(n - k)))
Мы также можем, например, объявить функцию binomial
, которая принимает два целочисленных параметра
n
и k
и вычисляет число сочетаний из n
по k
:
def binomial(n, k) return factorial(n) // (factorial(k) * factorial(n - k))
Тогда в нашей основной программе мы можем вызвать функцию binomial
для нахождения числа сочетаний:
print(binomial(n, k))
Вернемся к задаче нахождения наибольшего из двух или трех чисел. Функцию нахождения максимума из двух чисел можно написать так:
def max(a, b): if a > b: return a else: return b
Теперь мы можем реализовать функцию max3
, находящую максимум трех чисел:
def max3(a, b, c): return max(max(a, b), c)
Функция max3
дважды вызывает функцию max
для двух чисел:
сначала, чтобы найти максимум из a
и b
, потом чтобы найти максимум из этой
величины и c
.
Внутри функции можно использовать переменные, объявленные вне этой функции
def f(): print(a) a = 1 f()
Здесь переменной a
присваивается значение 1, и функция f
печатает это значение, несмотря на то, что выше функции f
эта переменная
не инициализируется. Но в момент вызова функции f
переменной a
уже присвоено значение, поэтому функция f
может вывести его на экран.
Такие переменные (объявленные вне функции, но доступные внутри функции) называются глобальными.
Но если инициализировать какую-то переменную внутри функции, использовать эту переменную вне функции не удастся. Например:
def f(): a = 1 f() print(a)
Получим NameError: name 'a' is not defined
. Такие переменные, объявленные внутри функции,
называются локальными. Эти переменные становятся недоступными после выхода из функции.
Интересным получится результат, если попробовать изменить значение глобальной переменной внутри функции:
def f(): a = 1 print(a) a = 0 f() print(a)
Будут выведены числа 1 и 0. То есть несмотря на то, что значение переменной a
изменилось внутри функции, то вне функции оно осталось прежним! Это сделано в целях
“защиты” глобальных переменных от случайного изменения из функции
(например, если функция будет вызвана из цикла по переменной i
, а в этой функции
будет использована переменная i
также для организации цикла, то эти переменные должны
быть различными). То есть если внутри функции модифицируется значение некоторой переменной,
то переменная с таким именем становится локальной переменной, и ее модификация не приведет
к изменению глобальной переменной с таким же именем.
Более формально: интерпретатор Питон считает переменную локальной, если внутри
нее есть хотя бы одна инструкция, модифицирующая значение переменной (это может быть
оператор =
, +=
и т.д., или использование этой переменной
в качестве параметра цикла for
, то эта переменная считается локальной
и не может быть использована до инициализации. При этом даже если инструкция,
модицифицирующая переменную никогда не будет выполнена: интерпретатор это проверить
не может, и переменная все равно считается локальной. Пример:
def f(): print (a) if False: a = 0 a = 1 f()
Возникает ошибка: UnboundLocalError: local variable 'a' referenced before assignment
.
А именно, в функции f
идентификатор a
становится локальной переменной,
т.к. в функции есть команда, модифицирующая переменную a
, пусть даже никогда и
не выполняющийся (но интерпретатор не может это отследить). Поэтому вывод переменной a
приводит к обращению к неинициализированной локальной переменной.
Чтобы функция могла изменить значение глобальной переменной, необходимо объявить эту переменную
внутри функции, как глобальную, при помощи ключевого слова global
:
def f(): global a a = 1 print (a) a = 0 f() print(a)
В этом примере на экран будет выведено 1 1, так как переменная a
объявлена, как глобальная,
и ее изменение внутри функции приводит к тому, что и вне функции переменная
будет доступна.
Тем не менее, лучше не изменять значения глобальных переменных внутри функции. Если функция должна поменять какую-то переменную, то как правило это лучше сделать, как значение, возвращаемое функцией.
Если нужно, чтобы функция вернула не одно значение, а два или более, то для этого функция может вернуть кортеж из двух или нескольких значений:
return (a, b)
Тогда результат вызова функции тоже нужно присваивать кортежу:
(n, m) = f(a, b)
Напишите функцию min4(a, b, c, d)
, вычисляющую минимум четырех чисел, которая
не содержит инструкции if
, а использует стандартную функцию
min
. Считайте четыре целых числа и выведите их минимум.
Ввод | Вывод |
---|---|
5 |
1 |
Даны четыре действительных числа: x1, y1, x2, y2. Напишите функцию
distance(x1, y1, x2, y2)
, вычисляющая расстояние
между точкой (x1,y1) и (x2,y2). Считайте четыре действительных числа
и выведите результат работы этой функции.
Ввод | Вывод |
---|---|
0 |
1.41421 |
Даны два действительных числа x и y. Проверьте, принадлежит ли точка с координатами
(x,y) заштрихованному квадрату (включая его границу). Если точка принадлежит квадрату, выведите слово YES
,
иначе выведите слово NO
. На рисунке сетка проведена с шагом 1.
Решение должно содержать функцию IsPointInSquare(x, y)
,
возвращающую True
, если точка принадлежит квадрату и False
, если не принадлежит.
Основная программа должна считать координаты точки, вызвать функцию IsPointInSquare
и в зависимости от возвращенного значения вывести на экран необходимое сообщение.
Функция IsPointInSquare
не должна содержать инструкцию if
.
Ввод | Вывод |
---|---|
0 |
YES |
3 |
NO |
Решите аналогичную задачу для такого квадрата:
Решение должно соответствовать требованиям для решения задачи C.
Ввод | Вывод |
---|---|
0 |
YES |
1 |
NO |
Даны пять действительных чисел: X, Y, Xc, Yc, R. Проверьте, принадлежит ли точка (X,Y) кругу с центром (Xc,Yc) и радиусом R.
Решение оформите в виде функции IsPointInCircle(x, y, xc, yc, r)
.
Решение должно соответствовать требованиям для решения задачи C.
Ввод | Вывод |
---|---|
0.5 |
YES |
0.5 |
NO |
Проверьте, принадлежит ли точка данной закрашенной области:
Решение оформите в виде функции bool IsPointInArea(x, y)
.
Решение должно соответствовать требованиям для решения задачи D.
Ввод | Вывод |
---|---|
-1 |
YES |
0 |
NO |
Эпиграф: def ShortStory(): print("У попа была собака, он ее любил.") print("Она съела кусок мяса, он ее убил,") print("В землю закопал и надпись написал:") ShortStory()
Как мы видели выше, функция может вызывать другую функцию. Но функция также может вызывать и саму себя! Рассмотрим это на примере функции вычисления факториала. Хорошо известно, что 0!=1, 1!=1. А как вычислить величину n! для большого n? Если бы мы могли вычислить величину (n-1)!, то тогда мы легко вычислим n!, поскольку n!=n(n-1)!. Но как вычислить (n-1)!? Если бы мы вычислили (n-2)!, то мы сможем вычислить и (n-1)!=(n-1)(n-2)!. А как вычислить (n-2)!? Если бы... В конце концов, мы дойдем до величины 0!, которая равна 1. Таким образом, для вычисления факториала мы можем использовать значение факториала для меньшего числа. Это можно сделать и в программе на C++:
def factorial (n): if n == 0: return 1 else: return n * factorial(n - 1)
Подобный прием (вызов функцией самой себя) называется рекурсией, а сама функция называется рекурсивной.
Рекурсивные функции являются мощным механизмом в программировании. К сожалению, они не всегда эффективны (об этом речь пойдет позже). Также часто использование рекурсии приводит к ошибкам, наиболее распространенная из таких ошибок – бесконечная рекурсия, когда цепочка вызовов функций никогда не завершается и продолжается, пока не кончится свободная память в компьютере. Пример бесконечной рекурсии приведен в эпиграфе к этому разделу. Две наиболее распространенные причины для бесконечной рекурсии:
if n == 0
, то factorial(0)
вызовет factorial(-1)
,
тот вызовет factorial(-2)
и т.д.
factorial(n)
будет
вызывать factorial(n)
, то также получиться бесконечная цепочка.
Поэтому при разработке рекурсивной функции необходимо прежде всего оформлять условия завершения рекурсии и думать, почему рекурсия когда-либо завершит работу.
Дано действительное положительное число a и целое неотрицательное число n.
Вычислите an не используя циклы и стандартную функцию pow
, а используя
рекуррентное соотношение an=a * a n-1.
Решение оформите в виде функции power(a, n)
.
Ввод | Вывод |
---|---|
2 |
8 |
Головоломка “Ханойские башни” состоит из трех стержней, пронумерованных числами 1, 2, 3. На стержень 1 надета пирамидка из n дисков различного диаметра в порядке возрастания диаметра. Диски можно перекладывать с одного стержня на другой по одному, при этом диск нельзя класть на диск меньшего диаметра. Необходимо переложить всю пирамидку со стержня 1 на стержень 3 за минимальное число перекладываний.
Напишите программу, которая решает головоломку; для данного числа дисков n
печатает последовательность перекладываний в формате
b c
, где b
— номер стержня с которого снимается данный диск,
c
— номер стержня на который надевается данный диск.
Например, строка 2 3
означает перемещение верхнего диска со стержня
2 на стержень 3. В одной строке печатается одна команда.
Программа должна вывести минимальный (по количеству произведенных операций) способ перекладывания пирамидки из данного числа дисков.
Указание: подумайте, как переложить пирамидку из одного диска? Из двух дисков? Из трех дисков? Из четырех дисков? Пусть мы научились перекладывать пирамидку из n дисков с произвольного стержня на любой другой, как переложить пирамидку из n+1 диска, если можно пользоваться решением для n дисков.
Напишите функцию move (n, x, y, z)
,
которая печатает последовательнось перекладываний дисков для перемещения
пирамидки высоты n
со стержня номер x
на стержень номер y
, используя в качестве промежуточного стержень z
.
Ввод | Вывод |
---|---|
2 |
1 2 |
Головоломка “Ханойские башни” состоит из трех стержней, пронумерованных числами 1, 2, 3. На стержень 1 надета пирамидка из n дисков различного диаметра в порядке возрастания диаметра. Диски можно перекладывать с одного стержня на другой по одному, при этом диск нельзя класть на диск меньшего диаметра. Необходимо переложить всю пирамидку со стержня 1 на стержень 3 за минимальное число перекладываний.
Напишите программу, которая решает головоломку; для данного числа дисков n
печатает последовательность перекладываний в формате
a b c
, где a
— номер перекладываемого диска,
b
— номер стержня с которого снимается данный диск,
c
— номер стержня на который надевается данный диск.
Например, строка 1 2 3
означает перемещение диска номер 1 со стержня
2 на стержень 3. В одной строке печатается одна команда.
Диски пронумерованы числами от 1 до n в порядке возрастания диаметров.
Программа должна вывести минимальный (по количеству произведенных операций) способ перекладывания пирамидки из данного числа дисков.
Указание: подумайте, как переложить пирамидку из одного диска? Из двух дисков? Из трех дисков? Из четырех дисков? Пусть мы научились перекладывать пирамидку из n дисков с произвольного стержня на любой другой, как переложить пирамидку из n+1 диска, если можно пользоваться решением для n дисков.
Напишите функцию move (n, x, y)
,
которая печатает последовательнось перекладываний дисков для перемещения
пирамидки высоты n
со стержня номер x
на стержень номер y
.
Ввод | Вывод |
---|---|
2 |
1 1 2 |