что означает init, print, res в паскале
В паскале нет такого
init — сокращение от initialization. В паскале такого вроде не было никогда. Оно уже в Delphi появилось.
print — такого нет, есть write/writeln
res — это вообще непонятно что.
Если это вопрос, то отвечай что это может быть как именем переменной/константы, так и именем процедуры/функции, структуры и тд.
print- обычно название процедуры
res- обозначение переменной (результат)
вот пример
procedure print(n: longint);
var
i: byte;
res: set of byte;
begin
res:= [];
n:= Abs(n);
while n <> 0 do begin
res:= res + [n mod 10];
n:= n div 10;
end;
res:= [0..9] — res;
for i:= 0 to 9 do
if i in res then Write(‘ ‘, i);
end;
Тема 9. СЛУЧАЙНЫЕ ВЕЛИЧИНЫ
Случайная величина, принимающая различные значения, которые можно записать в виде конечной или бесконечной последовательности, называется дискретной случайной величиной. Примеры: число очков, выпавших при бросании игральной кости; число родившихся детей в семье; число шаров, которые можно достать из урны и т. д.
Случайная величина, которая может принимать все значения из некоторого числового промежутка, называется непрерывной случайной величиной.
Примеры: прирост веса домашнего животного за месяц есть случайная величина, которая может принять значение из некоторого промежутка; прогнозируемая температура воздуха по области и т. д.
Закон распределения случайной величины
где x 1 , x 2 , …, x n — случайные величины, соответствующие полной группе событий, т. е. p 1 + p 2 + … + p n = 1.
При возрастании количества исходов полной группы событий закон распределения становится менее наглядным, и оценить наиболее вероятный исход становится достаточно трудно. Поэтому вводят характеристики случайных величин: математическое ожидание — ожидаемая величина в данном опыте, дисперсия — разброс значений.
Характеристики случайной величины
Математическим ожиданием М (Х) дискретной случайной величины Х называется сумма произведений всех возможных значений величины Х на соответствующие вероятности:
Дисперсией D (X) дискретной случайной величины Х называется математическое ожидание квадрата отклонения случайной величины Х от ее математического ожидания:
Средним квадратическим отклонением s (Х) случайной величины Х называется корень квадратный из ее дисперсии:
Интегральной функцией распределения непрерывной случайной величины Х называется функция F(x), равная вероятности того, что Х приняла значение, меньшее х:
Дифференциальной функцией распределения непрерывной случайной величины Х (или ее плотностью вероятности) называется функция f(x), равная производной интегральной функции:
Математическим ожиданием непрерывной случайной величины Х с плотностью вероятности f(x) называется величина несобственного интеграла (если он сходится):
Дисперсией непрерывной случайной величины Х, математическое ожидание которой М (Х) = а и функция f (x) является плотностью вероятности, называется величина несобственного интеграла (если он сходится):
Для непрерывной случайной величины Х среднее квадратическое отклонение s (Х) определяется как и для дискретной величины.
9.2. Некоторые законы распределения
Биномиальное распределение
Пусть производится n испытаний, причем вероятность появления события А в каждом испытании равна р и не зависит от исхода других испытаний (независимые испытания). Так как вероятность наступления события А в одном испытании равна р, то вероятность его ненаступления равна q = 1 — р.
Найдем вероятность того, что при n испытаниях событие А наступит m раз (m Ј n). Пусть событие А наступило в первых n испытаниях m раз и не наступило во всех последующих испытаниях. Это сложное событие можно написать в виде произведения:
Общее число сложных событий, в которых событие А наступает т раз, равно числу сочетаний из n элементов по m элементов. Так как эти сложные события несовместимы, то вероятность их суммы равна сумме вероятностей. При этом вероятность каждого сложного события равна pm Ч qn-m. Вероятность появления события А m раз в n испытаниях равна:
Закон биномиального распределения
Нормальное распределение
Закон распределения вероятностей непрерывной случайной величины Х называется нормальным, если ее дифференциальная функция f (x) определяется формулой:
где а совпадает с математическим ожиданием величины Х: а = М(Х), параметр s совпадает со средним квадратическим отклонением величины Х: s = s (Х).
График функции нормального распределения, как видно из рисунка, имеет вид куполообразной кривой, называемой Гауссовой, точка максимума имеет координаты (а; ). Значит, эта ордината убывает с возрастанием значения s (кривая «сжимается» к оси Ох) и возрастает с убыванием значения s (кривая «растягивается» в положительном направлении оси Оу). Изменение значений параметра а (при неизменном значении s) не влияет на форму кривой, а лишь перемещает кривую вдоль оси Ох.
Нормальное распределение с параметрами а = 0 и s = 1 называется нормированным. Дифференциальная функция в случае такого распределения будет:
Пусть случайная величина Х распределена по нормальному закону. Тогда вероятность того, что Х примет значение, принадлежащее интервалу (a; b):
Вопросы
1. Приведите примеры испытаний, результатом которых становятся дискретные случайные величины, непрерывные случайные величины.
2. Что отражают характеристики случайных величин — математическое ожидание, среднее квадратическое отклонение? Покажите на графике.
3. Обоснуйте справедливость равенства p 1 + p 2 + … + p n = 1, используемого в законе распределения случайных величин.
4. Приведите примеры испытаний, где для расчета вероятности могла бы использоваться формула Бернулли.
5. Почему в формуле Бернулли используется число сочетаний, а не, допустим, размещений?
6. Что означает фраза «значения распределены по нормальному закону»?
3. Трамвай
Фактически нужно промоделировать поездку трамвая. Когда пассажир входит в трамвай, если есть свободное место, и его `a_i-b_i\ >\ 0` , то он садится, иначе ищем среди сидящих пассажиров пассажира с минимальной разностью `a_i-b_i` . Если эта разность окажется меньше, чем у вошедшего, то этот пассажир должен уступить место вошедшему. Когда сидящий пассажир выходит из трамвая, то его место занимает пассажир с наибольшей положительной разностью `a_i-b_i` среди стоящих.
Для быстрого нахождения пассажира с минимальной разностью среди сидящих и максимальной разностью среди стоящих в C++ можно использовать класс STL set, который реализован на красно-черных деревьях. Реализовать эту структуру на Паскале очень сложно, но для данной задачи можно использовать гораздо более простое дерево сумм, которое позволяет находить `m` -ое по величине значение в наборе за логарифмическое время. Выглядит это дерево так:

В вершине с номером `k` хранится сумма значений в вершинах с номерами `2*k` и `2*k+1` . При изменении значений в листьях дерева в нижней строке, корректное состояние дерева восстанавливается за логарифмическое время.
Разность `a_i-b_i` не превышает `2\ 000\ 000` (отрицательные разности обрабатываются отдельно), поэтому храним дерево в массиве размером `4\ 194\ 303` , элементы с 1 по `2\ 097\ 151` хранят узлы дерева, а элементы с `2\ 097\ 152` по `4\ 194\ 303` – листья. Для обращения к нужному листу, хранящему количество пассажиров с некоторой разностью, нужно добавить к разности `2\ 097\ 152`
var sumtree:array[1..4194303] of longint; . k:=pass[j].a-pass[j].b+2097152; if pass[j].c=i then inc(sumtree[k]) < пассажир вошел >else dec(sumtree[k]); < пассажир вышел > < восстанавливаем корректность дерева >while k>1 do begin k:=k div 2; sumtree[k]:=sumtree[2*k]+sumtree[2*k+1]; end;
Для нахождения разности у `m` -го пассажира в отсортированном по разности наборе используем следующий код:
if sumtree[1] < m then difm:=0 < пассажиров с положительной разностью меньше m >else begin k:=1; mm:=m; while kdo begin k:=2*k+1; < переходим к правому потомку узла >if sumtree[k]then begin < в правом потомке менее mm пассажиров >mm:=mm-sumtree[k]; dec(k); < выбираем левого потомка >end; end; difm:=k-2097152; end;
После добавления или удаления пассажира проверяем как изменилась разность у `m` -го пассажира и изменяем сумму разностей сидящих пассажиров.
if pass[j].c=i then begin < пассажир вошел >if oldifmthen < занял чье-то место >sumd:=sumd+pass[j].a-pass[j].b-oldifm end else begin < пассажир вышел >if difmthen < на его место кто-то сел >sumd:=sumd-(pass[j].a-pass[j].b)+difm; end; oldifm:=difm;
Вместо написания быстрой сортировки пассажиров по номерам остановок, на которых они входят и выходят, можно сделать разделение пассажиров по спискам, соответствующим остановкам.
var sl:array [1..100001] of integer; < номера первых пассажиров в спиcках >pass:array [1..100000] of record a,b,c,d:longint; nextin,nextout:longint; < номер следующего в списке >end; . < Ввод и распределение по спискам >readln(N,M,S); for i:=1 to N do begin readln(pass[i].a,pass[i].b,pass[i].c,pass[i].d); pass[i].nextin:=sl[pass[i].c]; sl[pass[i].c]:=i; pass[i].nextout:=sl[pass[i].d]; sl[pass[i].d]:=i; end;
Обработка списка для каждой остановки и вычисление результата выглядит так:
res:=0; sumd:=0; < сумма разностей у сидящих >sumb:=0; < сумма b[i] у всех пассажиров трамвая >oldifm:=0; for i:=1 to S do begin j:=sl[i]; < индекс первого пассажира, вошедшего или вышедшего на i-ой остановке >while j<>0 do < список не закончился >begin if pass[j].a>pass[j].b then begin < модифицируем дерево сумм >. < пересчитываем сумму разностей сидящих пассажиров >. end; if pass[j].c=i then begin < пассажир вошел >sumb:=sumb+pass[j].b j:=pass[j].nextin; < к следующему в списке >end else begin < пассажир вышел >sumb:=sumb-pass[j].b; j:=pass[j].nextout; < к следующему в списке >end; end; res:=res+sumd+sumb; end; writeln(res);
В операторе res:=res+(sumd+sumb); при сложении суммы разностей `a_i-b_i` для сидящих (sumd) и суммы `b_i` для всех пассажиров (sumb) получается сложение сумма `a_i` для сидящих и сумма `b_i` для стоящих.
5.1. Теория¶
Подпрограмма — средство языка программирования, позволяющее упаковывать и параметризовать функциональность.
Как правило, подпрограмма — это именованный фрагмент программного кода, к которому можно обратиться из другого места программы, однако она может и не иметь имени (называясь в таком случае анонимной).
5.1.1. Основные понятия и механизм работы¶
5.1.1.1. Определение подпрограммы¶
Подпрограмма должна быть объявлена и в общем случае содержать:
- имя;
- список имен и типов передаваемых параметров (необязательно);
- тип возвращаемого значения (необязательно).
Если подпрограмма возвращает значение вызывающему коду (одно или несколько), она называется функцией, иначе — процедурой.
5.1.1.2. Вызов подпрограммы¶
Для того, чтобы использовать ранее определенную подпрограмму, необходимо в требуемом месте кода произвести ее вызов, указав:
- указать имя подпрограммы;
- передать требуемые аргументы (значения параметров).
Код, вызвавший подпрограмму, передает ей управление и ожидает завершения выполнения.
Подпрограмма также может вызывать сама себя, т.е. выполняться рекурсивно.
В настоящее время наиболее часто встречаются следующие способы передачи аргументов:
Для переменной, переданной по значению создается локальная копия и любые изменения, которые происходят в теле подпрограммы с переданной переменной, на самом деле, происходят с локальной копией и никак не сказываются на самой переменной.
Изменения, которые происходят в теле подпрограммы с переменной, переданной по ссылке, происходят с самой переданной переменной.
5.1.1.3. Механизм работы¶
Большинство современных языков программирования для управления вызовом подпрограмм используют стек вызовов.
Примерный цикл работы стека вызова следующий (Видео 5.1.1, Видео 5.1.2):
- Вызов подпрограммы создает запись в стеке; каждая запись может содержать информацию о данных вызова (аргументах, результате, а также адресе возврата).
- Когда подпрограмма завершается, запись удаляется из стека и программа продолжает выполняться, начиная с адреса возврата.
Видео 5.1.1 — Cтек вызовов (пример, Нетология)
Your browser does not support the video tag.
Видео 5.1.2 — Cтек вызовов (пример, Stepik.org)
5.1.1.4. Преимущества и недостатки¶
Главное назначение подпрограмм сегодня — структуризация программы с целью удобства ее понимания и сопровождения.
Преимущества использования подпрограмм:
- декомпозиция сложной задачи на несколько более простых подзадач: это один из двух главных инструментов структурного программирование (второй — структуры данных);
- уменьшение дублирования кода и возможность повторного использования кода в нескольких программах — следование принципу DRY «не повторяйся» (англ.Don’t Repeat Yourself);
- распределение большой задачи между несколькими разработчиками или стадиями проекта;
- сокрытие деталей реализации от пользователей подпрограммы;
- улучшение отслеживания выполнения кода (большинство языков программирования предоставляет стек вызовов подпрограмм).
Недостатком использования подпрограмм можно считать накладные расходы на вызов подпрограммы, однако современные трансляторы стремятся оптимизировать данный процесс.
5.1.2. Функции в Python¶
В Python нет формального разделения подпрограмм на функции и процедуры (как, например, в Паскале или Си), и процедурой можно считать функцию, возвращающую пустое значение — в остальном используется единственный термин — функция.
Для объявления функции в Python используется ключевое слово def :
def function_name([parameters]): # `parameters`: параметры функции (через запятую) suite # Тело функции
На имя функции в Python накладываются такие же ограничения, как и на прочие идентификаторы .
Соглашение рекомендует использовать:
- змеиный_регистр (англ. snake_case) для наименования функций: my_function ;
- пустые строки для отделения функций, а большие блоки кода помещать внутрь функций;
- строки документации .
В Python все данные — объекты, при вызове в функцию передается ссылка на этот объект. При этом, мутирующие объекты передаются по ссылке, немутирующие — по значению.
Функция в Python может возвращать результат своего выполнения, используя оператор return (например, return 5 ). В случае, если он не был указан или указан пустой оператор return , возвращается специальное значение None .
При встрече оператора return в коде Python немедленно завершает выполнение функции, аналогично break для циклических конструкций.
Пример определения и вызова функции приведен в Листинге 5.1.1 и на Рисунке 5.1.1.
Листинг 5.1.1 — Пример определения и вызова функции | скачать ¶
# Функция для вычисления гипотенузы # Имя: hypot, 2 параметра: x, y # return возвращает результат работы функции вызвавшему def hypot(x, y): return (x**2 + y**2)**0.5 z = hypot(3, 4) # Передача в функцию 2-х аргументов: 3 и 4 print(z) # 5.0 a = 5 b = 12 print(hypot(a, b)) # 13.0 - результат функции может быть использован сразу

Рисунок 5.1.1 — Передача параметров и возвращение результата функции ¶
Python, как и многие другие языки, позволяет создавать собственные (пользовательские) функции, среди которых можно выделить четыре типа (Листинг 5.1.2):
Доступны из любой точки программного кода в том же модуле или из других модулей.
Объявляются внутри других функций и видны только внутри них: используются для создания вспомогательных функций, которые нигде больше не используются.
Не имеют имени и объявляются в месте использования. В Python и представлены лямбда-выражениями.
Функции, ассоциированные с каким-либо объектом (например, list.append() , где append() — метод объекта list ).
Листинг 5.1.2 — Четыре типа функций в Python | скачать ¶
class Car: def move(self, x): # Метод (3) self.x += x def sum_of_cubes(x, y): # Глобальная функция (1) # Локальная функция (2) (ее "видит" только код внутри sum_of_cubes()) def cube(a): return a**3 return cube(x) + cube(y) # return возвращает результат выполнения тому, # кто вызвал эту функцию players = ["name": "Юрий", "rank": 5>, "name": "Сергей", "rank": 3>, "name": "Максим", "rank": 4>] # Анонимная функция (4) (лямбда-выражение) # В функции sorted() используется для определения порядка сортировки print(sorted(players, key=lambda player: player["name"])) # Сортировка по name # [, , ] print(sorted(players, key=lambda player: player["rank"])) # Сортировка по rank # [, , ]
5.1.3. Глобальные и локальные функции¶
5.1.3.1. Параметры и аргументы¶
5.1.3.1.1. Позиционные и ключевые параметры/аргументы¶
Все параметры, указываемые в Python при объявлении и вызове функции делятся на:
-
позиционные: указываются простым перечислением:
def function_name(a, b, c): # a, b, c - 3 позиционных параметра pass
def function_name(key=value, key2=value2): # key, key2 - 2 позиционных аргумента pass # value, value2 - их значения по умолчанию
Позиционные и ключевые аргументы могут быть скомбинированы. Синтаксис объявления и вызова функции зависит от типа параметра, однако позиционные параметры (и соответствующие аргументы) всегда идут перед ключевыми:
def example_func(a, b, c): # можно : 'a', 'b', 'c' - позиционные параметры pass def example_func(a, b, c=3): # можно : 'a', 'b' - позиционные параметры, pass # 'c' - ключевой параметр def example_func(a=1, b=2, c=3): # можно : 'a', 'b', 'c' - ключевые параметры pass def example_func(a=1, с, b=2): # нельзя: ключевой параметр 'a' pass # идет раньше позиционнных
def example_func(a, b, c=3): # a, b - позиционные параметры, c - ключевой параметр pass # Вызовы функции example_func(1, 2, 5) # можно : аргументы 1, 2, 5 распределяются # позиционно по параметрам 'a', 'b', 'c' example_func(1, 2) # можно : аргументы 1, 2 распределяются позиционно # по параметрам 'a', 'b' # в ключевой параметр 'c' аргумент # не передается, используется значение 3 example_func(a=1, b=2) # можно : аналогично example_func(1, 2), # все аргументы передаются по ключу example_func(b=2, a=1) # можно : аналогично example_func(a=1, b=2), # если все позиционные параметры заполнены как # ключевые аргументы, можно не соблюдать порядок example_func(c=5, b=2, a=1) # можно : аналогично example_func(1, 2), # аргументы передаются по ключу example_func(1) # нельзя: для позиционного аргумента 'b' # не передается аргумент example_func(b=1) # нельзя: для позиционного аргумента 'a' # не передается аргумент
Преимущества ключевых параметров:
- нет необходимости отслеживать порядок аргументов;
- у ключевых параметров есть значение по умолчанию, которое можно не передавать.
Пример функции со смешанными типами параметров приведен в Листинге 5.1.3.
Листинг 5.1.3 — Позиционные и ключевые параметры функций в Python | скачать ¶
# Функция выдает запрос и # - возвращает True в случае положительного ответа # - возвращает False в случае отрицательного ответа # - возвращает False если не получает ответ за [retries] попыток def ask_user(prompt, retries=3, hint="Ответьте, ДА или НЕТ?"): while True: retries -= 1 ok = input(prompt + " -> ").upper() if ok in ("Д", "ДА"): return True elif ok in ("Н", "НЕТ"): return False if retries 0: print("Не смог получить нужный ответ, считаю за отказ.") return False print(hint) # С ключевыми параметрами будут доступны также следующие варианты: # ask_user("Сохранить файл?", 0) # ask_user("Сохранить файл?", retries=1) # ask_user("Сохранить файл?", 2, "Жми Д или Н. ") # и др. if ask_user("Сохранить файл?"): print("Сохранил!") else: print("Не сохранил.") # ------------- # Пример вывода: # # Сохранить файл? -> Не знаю # Ответьте, ДА или НЕТ? # Сохранить файл? -> Да # Сохранил!
5.1.3.1.2. Упаковка и распаковка аргументов¶
В ряде случаев бывает полезно определить функцию, способную принимать любое число аргументов. Так, например, работает функция print() , которая может принимать на печать различное количество объектов и выводить их на экран.
Достичь такого поведения можно, используя механизм упаковки аргументов, указав при объявлении параметра в функции один из двух символов:
- * : все позиционные аргументы начиная с этой позиции и до конца будут собраны в кортеж;
- ** : все ключевые аргументы начиная с этой позиции и до конца будут собраны в словарь.
Пример упаковки аргументов приведен в Листинге 5.1.4.
Листинг 5.1.4 — Упаковка аргументов | скачать ¶
# При упаковке аргументов все переданные позиционные аргументы # будут собраны в кортеж 'order', а ключевые - в словарь 'info' def print_order(*order, **info): print("Музыкальная библиотека №1\n") # Словарь 'infos' должен содержать ключи 'author' и 'birthday' for key, value in sorted(info.items()): print(key, ":", value) # Кортеж 'order' содержит все наименования произведений print("Вы выбрали:") for item in order: print(" -", item) print("\nПриходите еще!") print_order("Славянский марш", "Лебединое озеро", "Спящая красавица", "Пиковая дама", "Щелкунчик", author="П.И. Чайковский", birthday="07/05/1840") # ------------- # Пример вывода: # # Музыкальная библиотека №1 # # author : П.И. Чайковский # birthday : 07/05/1840 # Вы выбрали: # - Славянский марш # - Лебединое озеро # - Спящая красавица # - Пиковая дама # - Щелкунчик # # Приходите еще!
Python также предусматривает и обратный механизм — распаковку аргументов, используя аналогичные обозначения перед аргументом:
- * : кортеж/список распаковывается как отдельные позиционные аргументы и передается в функцию;
- ** : словарь распаковывается как набор ключевых аргументов и передается в функцию.
Пример распаковки аргументов приведен в Листинге 5.1.5.
Листинг 5.1.5 — Распаковка аргументов | скачать ¶
# Площадь треугольника по формуле Герона # # Данный пример функции предназначен для демонстрации распаковки # Не проектируйте функцию таким образом - # расчетная функция должна возвращать число, а не строку! def heron_area_str(a, b, c, units="сантиметры", print_error=True): if a + b c or a + c b or b + c a: if print_error: return "Проверьте введенные стороны треугольника!" return p = (a + b + c) / 2 s = (p * (p - a) * (p - b) * (p - c)) ** 0.5 return "<> <>".format(s, units) abc = [3, 4, 5] params = dict(print_error=True, units="см.") # При распаковке аргументов список 'abc' будет распакован в # позиционные аргументы, а словарь 'params' - ключевые print(heron_area_str(*abc, **params)) # ------------- # Пример вывода: # # 6.0 см.
5.1.3.2. Область видимости¶
Область видимости — область программы, где определяются идентификаторы, и транслятор выполняет их поиск. За пределами области видимости тот же самый идентификатор может быть связан с другой переменной, либо быть свободным (не связанным ни с какой из них).
В Python выделяется четыре области видимости:
Собственная область внутри инструкции def .
Область в пределах вышестоящей инструкции def .
Область за пределами всех инструкций def — глобальная для всего модуля.
«Системная» область модуля builtins : содержит предопределенные идентификаторы, например, функцию max() и т.п.
Локальная и нелокальная области видимости являются относительными, глобальная и встроенная — абсолютными.
- идентификатор может называться локальным, глобальным и т.д., если имеет соответствующую область видимости;
- функции образуют локальную область видимости, а модули – глобальную;
- чем ближе область к концу списка, тем более она открыта (ее содержимое доступно для более закрытых областей видимости; например, глобальные идентификаторы и предопределенные имена могут быть доступны в локальной области видимости функции, но не наоборот).
В Листинге 5.1.6 приведен пример четырех областей видимости.
Листинг 5.1.6 — 4 области видимости в Python | скачать ¶
def min_of_cubes(x, y): # Идентификаторы 'x' и 'y' являются: # - локальными для min_of_cubes() # - нелокальными для cube() def cube(a): return a**3 # 'a' - локальный идентификатор функции cube() return min(cube(x), cube(y), cube(c)) # Функция min() находится # во встроенной области # видимости и видна везде # Идентификаторы 'a', 'b' и 'c' имеют глобальную область видимости a, b, c = 2, 3, 4 print(min_of_cubes(a, b)) # 8
Схема разрешения имен в языке Python называется правилом LEGB (Local, Enclosed, Global, Built-in) 6: когда внутри функции выполняется обращение к неизвестному имени, интерпретатор пытается отыскать его в четырех областях видимости по очереди до первого нахождения.
В Листингах 5.1.7 (а-д) приведены некоторые случае выполнения LEGB-правила.
Листинг 5.1.7 (а) — Области видимости в Python (пример) | скачать ¶
# Увеличиваем переменную 'a' локально def sum_of_2(a, b): a += 2 return a + b a, b = 3, 4 print(sum_of_2(a, b)) # 9 print(a, b) # 3 4
Листинг 5.1.7 (б) — Области видимости в Python (пример) | скачать ¶
# Добавляем к сумме локальную переменную 'x' def sum_of_2(a, b): x = 5 return a + b + x a, b = 3, 4 print(sum_of_2(a, b)) # 12 print(a, b, x) # NameError: name 'x' is not defined
Листинг 5.1.7 (в) — Области видимости в Python (пример) | скачать ¶
# Пробуем увеличить глобальную переменную 'c' def sum_of_2(a, b): c *= 5 # UnboundLocalError: local variable 'c' referenced before assignment return a + b + с a, b, c = 3, 4, 5 print(sum_of_2(a, b))
Листинг 5.1.7 (г) — Области видимости в Python (пример) | скачать ¶
# Пробуем изменить глобальную переменную 'c' def sum_of_2(a, b): с = 10 return a + b + с a, b, c = 3, 4, 5 print(sum_of_2(a, b)) # 17 print(a, b, c) # 3 4 5
Листинг 5.1.7 (д) — Области видимости в Python (пример) | скачать ¶
# Используем глобальную переменную 'c', не имея соответствующего параметра def sum_of_2(a, b): return a + b + c a, b, c = 3, 4, 5 print(sum_of_2(a, b)) # 12
По умолчанию, идентификаторы из другой области видимости доступны только для чтения, а, при попытке присвоения, функция создает локальный идентификатор.
Если необходимо изменять в функции переменные более закрытой области видимости, существует 3 способа:
- использовать инструкцию global : сообщая, что функция будет изменять один или более глобальных идентификаторов;
- использовать инструкцию nonlocal : сообщая, что вложенная функция будет изменять один или более идентификаторов внешних функций;
- передать мутирующий аргумент в качестве параметра функции.
В Листингах 5.1.8, 5.1.9 и 5.1.10 приведены примеры использования ключевых слов global , nonlocal , а также передачи мутирующего аргумента.
Листинг 5.1.8 — Использование global позволяет менять значение глобальной переменной внутри функции | скачать ¶
def func(): # 'value' создается как локальный идентификатор value = 100 def func_with_global(): global value # global указывает, что нужно использовать 'value' # из глобальной области видимости value = 100 value = 0 func() print(value) # 0 func_with_global() print(value) # 100
Листинг 5.1.9 — Использование nonlocal позволяет менять значение нелокальной переменной внутри вложенной функции | скачать ¶
def func(): def inner_func(): # 'value' создается как локальный идентификатор inner_func() value = 100 def inner_func_with_nonlocal(): nonlocal value # Благодаря nonlocal используется 'value' из func() value = 100 value = 10 inner_func() print(value) # 10 inner_func_with_nonlocal() print(value) # 100 value = 0 func() print("global value =", value) # 0
Листинг 5.1.10 — Передача мутирующего аргумента в функцию | скачать ¶
# Функция принимает список, и добавляет сумму элементов в конец списка # # Изменение внутри функции мутирующего объекта приводит к его # изменению и в вызывающем коде def sum_list(lst): lst.append(sum(lst)) my_list = [1, 2, 3, 4, 5] sum_list(my_list) print(my_list) # [1, 2, 3, 4, 5, 15]
К изменению переменных из другой области видимости необходимо относиться крайне осторожно и прибегать только в случае крайней необходимости.
В большинстве случаев необходимо придерживаться концепции изолированного доступа через области видимости.
В связи с тем, что мутирующие аргументы могут быть изменены внутри функции, стоит аккуратно использовать их в качестве значений ключевых аргументов по умолчанию, которые создаются при инициализации функции (Листинг 5.1.11).
Листинг 5.1.11 — Мутирующие аргументы сохраняют значения после нескольких вызовов | скачать ¶
# Функция принимает число и список, к которому его нужно добавить # Если список не передан, число должно оказаться в пустом списке # 1. Данная реализация не учитывает единовременное создание значений # ключевых аргументов, и приводит к неожиданным эффектам def change_list(a, lst=[]): lst.append(a) return lst my_list = change_list(1) my_list = change_list(2) my_list = change_list(3) print(my_list) # [1, 2, 3] # Та же функция, реализованная правильно def change_list_2(a, lst=None): L = lst if L is None: L = [] L.append(a) return L my_list = change_list_2(1) my_list = change_list_2(2) my_list = change_list_2(3) print(my_list) # [3]
5.1.3.3. Возврат нескольких значений¶
Часто из функции необходимо вернуть несколько значений (например, в Паскале, для этого используются выходные параметры с ключевым словом var ). Одним из лучших способов для этого в Python является возврат кортежа с несколькими значениями (Листинг 5.1.12).
Листинг 5.1.12 — Возврат нескольких значений в виде кортежа | скачать ¶
# Функция выполняет решение квадратного уравнения, # сообщая вызывающему коду, сколько решений удалось получить # в формате (num, (x1, . )), # где в начале идет количество решений, а в отдельном кортеже сами решения def solve_equation(a, b, c): d = b**2 - 4 * a * c if d 0: return (0, ()) elif d == 0: x = -b / (2*a) return (1, (x, )) else: x1 = (-b - d**0.5) / (2*a) x2 = (-b + d**0.5) / (2*a) return (2, (x1, x2)) # Рекурсивный вариант вычисления факториала a = int(input("a = ")) b = int(input("b = ")) c = int(input("c = ")) num, x = solve_equation(a, b, c) print("\nРезультат решения: ", end="") if num == 0: print("решений нет") elif num == 1: print("x =", x[0]) elif num == 2: print("x1 =", x[0], " x2 =", x[1]) else: print("ошибка!") # ------------- # Пример вывода: # a = 1 # b = 4 # c = 3 # # Результат решения: x1 = -3.0 x2 = -1.0
Прочие способы (например, изменение глобальных переменных) не рекомендуются 7.
5.1.3.4. Рекурсия¶
Рекурсия — вызов функции внутри самой себя, непосредственно (простая рекурсия) или через другие функции (сложная или косвенная рекурсия)
Количество вложенных вызовов функции или процедуры называется глубиной рекурсии. Рекурсивная программа позволяет описать повторяющееся или даже потенциально бесконечное вычисление, причем без явных повторений частей программы и использования циклов.
Не рекомендуется использовать рекурсию, если такая функция может привести или приводит к большой глубине рекурсии — лучше заменить ее циклической конструкцией. Рекурсивный вызов требуется некоторое количество оперативной памяти компьютера, и при чрезмерно большой глубине рекурсии может наступить переполнение стека (англ. англ. Stack Overflow) вызовов.
В Листинге 5.1.13 приведен пример реализации рекурсивной функции.
Листинг 5.1.13 — Рекурсивные функции | скачать ¶
# Вычисление факториала, используя цикл def factorial_1(x): res = 1 for i in range(x): res *= i + 1 return res # Рекурсивный вариант вычисления факториала def factorial_2(x): if x == 1: return 1 else: return x * factorial_2(x - 1) print(factorial_1(5)) # 120 print(factorial_2(5)) # 120
5.1.3.5. Строки документации¶
Документирование кода — важный аспект, от которого зависит читаемость и быстрота понимания кода всеми участниками команды разработки.
Python предоставляет возможность добавить документацию (описание) к любой функции, используя строки документирования — это обычные строки тройных кавычках «»» , которые следуют сразу за строкой с инструкцией def перед программным кодом функции и воспринимаются транслятором специальным образом.
Соглашения по документированию функций содержатся в документе PEP 257.
Верно оформленная строка документации является краткой справкой по функции, которую можно увидеть, вызвав метод __doc__ , или функцию help() .
В Листинге 5.1.14 приведен пример написания документации к функции.
Листинг 5.1.14 — Документирование функции в Python | скачать ¶
# Для небольшой функции можно использовать однострочное документирование # Как правило, это описание действия в повелительном наклонении def sqrt(x): """Вернуть квадратный корень из 'x'.""" return x**0.5 # Если функция объемная, используется многострочное документирование def heron(a, b, c): """Вернуть площадь треугольника по формуле Герона. Параметры: - a, b, c (float): стороны треугольника. Результат: - float: значение площади. - None: если треугольник не существует. """ if not (a + b > c and a + c > b and b + c > a): return p = (a + b + c) / 2 return sqrt(p * (p - a) * (p - b) * (p - c)) # Справочную информацию о функции можно получить 2 способами: # heron.__doc__ или help(heron) print(heron.__doc__) params = [float(x) for x in input('Введите стороны (a b c): ').split()] s = heron(*params) if s: print('S = '.format(s)) else: print('Треугольник не существует!') # ------------- # Пример вывода: # Вернуть площадь треугольника по формуле Герона. # # Аргументы: # - a, b, c (float): стороны треугольника. # # Результат: # - float: значение площади. # - None: если треугольник не существует. # # Введите стороны (a b c): 3 4 5 # S = 6.00
5.1.4. Анонимные функции¶
Python поддерживает синтаксис, позволяющий определять анонимные функции (лямбда-функции или лямбда-выражения):
lambda parameters: expression
- часть parameters является необязательной, и если она присутствует, то обычно представляет собой простой список имен переменных, разделенных запятыми (позиционных аргументов);
- выражение expression не может содержать условных инструкций или циклов (условные выражения — допустимы), а также не может содержать инструкцию return ;
- результатом лямбда-выражения является анонимная функция.
Когда лямбда-функция вызывается, она возвращает результат вычисления выражения expression .
Пример записи лямбда-функции приведен в Листинге 5.1.15.
Листинг 5.1.15 — Лямбда-функции | скачать ¶
# Лямбда-функции, как правило, используются в случаях, где использование # функции не требует наличия у нее имени # # Если это требуется - лучше объявить обычную функцию, используя def # 1. Обычная и лямбда-функция выполняют одно и то же действие, # разница в синтаксисе def sqr1(x): return x**2 sqr2 = lambda x: x**2 # Если лямбда-выражение привязывается к идентификатору - # - оно не нужно; используйте обычные функции print(sqr1(6)) # 36 print(sqr2(6)) # 36 # 2. Лямбда-выражения удобно использовать для элементов функционального программирования # Эл-ты из таблицы Менделеева (номер группы, порядковый номер, наименование) elements = [(2, 12, "Mg"), (1, 11, "Na"), (1, 3, "Li"), (2, 4, "Be")] elements.sort(key=lambda x: x[1]) # Сортировка порядковому номеру print(elements) # [(1, 3, 'Li'), (2, 4, 'Be'), (1, 11, 'Na'), (2, 12, 'Mg')] # Функции max и min имеют ключевой аргумент key - параметр сортировки # key - ссылка на функцию, имеющую 1 возвращающую значение, по которому следует # сравнивать величины # 2.1. Используя лямбда-выражение lst = ["Java", "Algol", "C++"] print(max(lst, key=lambda x: x.count("a"))) # Элемент lst, в котором # больше всего "a" # 2.2. Тоже самое возможно с использованием обычной функции def find_a(x): return x.find("a") print(min(lst, key=find_a)) # Элемент lst, в котором # "a" находится левее
5.1.5. Побочный эффект¶
Побочный эффект (англ. Side Effect) — любые действия программы, изменяющие среду выполнения (англ. Execution Environment).
К побочным эффектам выполнения функции можно отнести:
- изменение данных в памяти;
- чтение/запись файла или устройства;
- ввод/вывод значений;
- самостоятельную реакцию на исключительные ситуации;
- и др.
Часто функции с побочным эффектом при вызове несколько раз с одними и теми же аргументами в качестве результата возвращают разные значения. Пример — стандартная функция генерации случайного числа.
Создавая функцию, необходимо избегать побочных эффектов — такие функции легче тестируются и не содержат скрытых действий. Один из примеров такой функции — функция solve_equation() в Листинге 5.1.12 .
Естественно, что полностью избежать побочных эффектов невозможно. В таких случаях необходимо локализовать участки кода с побочным эффектом в отдельные функции (Листинге 5.1.16).
Листинг 5.1.16 — Локализация кода с побочным эффектом в отдельные функции | скачать ¶
def heron(a, b, c): """Вернуть площадь треугольника по формуле Герона. Параметры: - a, b, c (float): стороны треугольника. Результат: - float: значение площади. - None: если треугольник не существует. """ if not (a + b > c and a + c > b and b + c > a): return p = (a + b + c) / 2 return (p * (p - a) * (p - b) * (p - c))**0.5 def input_data(): """Запросить стороны треугольника с клавиатуры. Результат: - tuple of float: (a, b, c). Побочный эффект! """ return (float(x) for x in input('Введите стороны (a b c): ').split()) def print_res(res): """Вывести на экран 'res'. Параметры: - res (float): площадь треугольника. Побочный эффект! """ if res: print('S = '.format(res)) else: print('Треугольник не существует!') a, b, c = input_data() res = heron(a, b, c) print_res(res)
Решая задачу, необходимо следить чтобы каждая проектируемая функция выполняла минимальную, логически полную задачу.
Например, если необходимо вычислить сумму и среднее значение элементов списка, правильнее будет создать 2 функции для каждого вычисления, а не одну; при этом ввод и вывод значений также оформить в виде отдельных функций.
Sebesta, W.S Concepts of Programming languages. 10E; ISBN 978-0133943023.
Саммерфилд М. Программирование на Python 3. Подробное руководство. — М.: Символ-Плюс, 2009. — 608 с.: ISBN: 978-5-93286-161-5.
Лучано Рамальо. Python. К вершинам мастерства. — М.: ДМК Пресс , 2016. — 768 с.: ISBN: 978-5-97060-384-0, 978-1-491-94600-8.
A Beginner’s Guide to Python’s Namespaces, Scope Resolution, and the LEGB Rule. URL: http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html.
How do I write a function with output parameters (call by reference). URL: https://docs.python.org/3/faq/programming.html#id17.
Версия: 2024. Обновлено: 28.12.2023.