0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<h2>Введение</h2>
1
<h2>Введение</h2>
2
<p>Существует несколько парадигм в программировании, например, ООП, функциональная, императивная, логическая, да много их. Мы будем говорить про функциональное программирование.</p>
2
<p>Существует несколько парадигм в программировании, например, ООП, функциональная, императивная, логическая, да много их. Мы будем говорить про функциональное программирование.</p>
3
<p>Предпосылками для полноценного функционального программирования в Python являются: функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений.</p>
3
<p>Предпосылками для полноценного функционального программирования в Python являются: функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений.</p>
4
<p>Сегодня познакомимся с простыми элементами, а сложные конструкции будут в других уроках.</p>
4
<p>Сегодня познакомимся с простыми элементами, а сложные конструкции будут в других уроках.</p>
5
<h2>Теория в теории</h2>
5
<h2>Теория в теории</h2>
6
<p>Как и в разговоре об ООП, так и о функциональном программировании, мы стараемся избегать определений. Все-таки четкое определение дать тяжело, поэтому здесь четкого определения не будет. Однако! Хотелки для функционального языка выделим:</p>
6
<p>Как и в разговоре об ООП, так и о функциональном программировании, мы стараемся избегать определений. Все-таки четкое определение дать тяжело, поэтому здесь четкого определения не будет. Однако! Хотелки для функционального языка выделим:</p>
7
<ul><li>Функции высшего порядка</li>
7
<ul><li>Функции высшего порядка</li>
8
<li>Чистые функции</li>
8
<li>Чистые функции</li>
9
<li>Неизменяемые данные</li>
9
<li>Неизменяемые данные</li>
10
</ul><p>Это не полный список, но даже этого хватает чтобы сделать "красиво". Если читателю хочется больше, то вот расширенный список:</p>
10
</ul><p>Это не полный список, но даже этого хватает чтобы сделать "красиво". Если читателю хочется больше, то вот расширенный список:</p>
11
<ul><li>Функции высшего порядка</li>
11
<ul><li>Функции высшего порядка</li>
12
<li>Чистые функции</li>
12
<li>Чистые функции</li>
13
<li>Неизменяемые данные</li>
13
<li>Неизменяемые данные</li>
14
<li>Замыкания</li>
14
<li>Замыкания</li>
15
<li>Ленивость</li>
15
<li>Ленивость</li>
16
<li>Хвостовая рекурсия</li>
16
<li>Хвостовая рекурсия</li>
17
<li>Алгебраические типы данных</li>
17
<li>Алгебраические типы данных</li>
18
<li>Pattern matching</li>
18
<li>Pattern matching</li>
19
</ul><p>Постепенно рассмотрим все эти моменты и как использовать в Python.</p>
19
</ul><p>Постепенно рассмотрим все эти моменты и как использовать в Python.</p>
20
<p>А сегодня кратко, что есть что в первом списке.</p>
20
<p>А сегодня кратко, что есть что в первом списке.</p>
21
<h3>Чистые функции</h3>
21
<h3>Чистые функции</h3>
22
<p>Чистые функции не производят никаких наблюдаемых побочных эффектов, только возвращают результат. Не меняют глобальных переменных, ничего никуда не посылают и не печатают, не трогают объектов, и так далее. Принимают данные, что-то вычисляют, учитывая только аргументы, и возвращают новые данные.</p>
22
<p>Чистые функции не производят никаких наблюдаемых побочных эффектов, только возвращают результат. Не меняют глобальных переменных, ничего никуда не посылают и не печатают, не трогают объектов, и так далее. Принимают данные, что-то вычисляют, учитывая только аргументы, и возвращают новые данные.</p>
23
<p>Плюсы:</p>
23
<p>Плюсы:</p>
24
<ul><li>Легче читать и понимать код</li>
24
<ul><li>Легче читать и понимать код</li>
25
<li>Легче тестировать (не надо создавать "условий")</li>
25
<li>Легче тестировать (не надо создавать "условий")</li>
26
<li>Надежнее, потому что не зависят от "погоды" и состояния окружения, только от аргументов</li>
26
<li>Надежнее, потому что не зависят от "погоды" и состояния окружения, только от аргументов</li>
27
<li>Можно запускать параллельно, можно кешировать результат</li>
27
<li>Можно запускать параллельно, можно кешировать результат</li>
28
</ul><h3>Неизменяемые данные</h3>
28
</ul><h3>Неизменяемые данные</h3>
29
<p>Неизменяемые (иммутабельные) структуры данных - это коллекции, которые нельзя изменить. Примерно как числа. Число просто есть, его нельзя поменять. Также и неизменяемый массив - он такой, каким его создали, и всегда таким будет. Если нужно добавить элемент - придется создать новый массив.</p>
29
<p>Неизменяемые (иммутабельные) структуры данных - это коллекции, которые нельзя изменить. Примерно как числа. Число просто есть, его нельзя поменять. Также и неизменяемый массив - он такой, каким его создали, и всегда таким будет. Если нужно добавить элемент - придется создать новый массив.</p>
30
<p>Преимущества неизменяемых структур:</p>
30
<p>Преимущества неизменяемых структур:</p>
31
<ul><li>Безопасно разделять ссылку между потоками</li>
31
<ul><li>Безопасно разделять ссылку между потоками</li>
32
<li>Легко тестировать</li>
32
<li>Легко тестировать</li>
33
<li>Легко отследить жизненный цикл (соответствует data flow)</li>
33
<li>Легко отследить жизненный цикл (соответствует data flow)</li>
34
</ul><p><a>theory-source</a></p>
34
</ul><p><a>theory-source</a></p>
35
<h3>Функции высшего порядка</h3>
35
<h3>Функции высшего порядка</h3>
36
<p>Функцию, принимающую другую функцию в качестве аргумента и/или возвращающую другую функцию, называют<strong>функцией высшего порядка</strong>:</p>
36
<p>Функцию, принимающую другую функцию в качестве аргумента и/или возвращающую другую функцию, называют<strong>функцией высшего порядка</strong>:</p>
37
<p>Рассмотрели теорию, начнем переходить к практике, от простого к сложному.</p>
37
<p>Рассмотрели теорию, начнем переходить к практике, от простого к сложному.</p>
38
<h2>Списковые включения или генератор списка</h2>
38
<h2>Списковые включения или генератор списка</h2>
39
<p>Рассмотрим одну конструкцию языка, которая поможет сократить количество строк кода. Не редко уровень программиста на Python можно определить с помощью этой конструкции.</p>
39
<p>Рассмотрим одну конструкцию языка, которая поможет сократить количество строк кода. Не редко уровень программиста на Python можно определить с помощью этой конструкции.</p>
40
<p>Пример кода:</p>
40
<p>Пример кода:</p>
41
<p>Цикл с условием, подобные встречаются не редко. А теперь попробуем эти 5 строк превратить в одну:</p>
41
<p>Цикл с условием, подобные встречаются не редко. А теперь попробуем эти 5 строк превратить в одну:</p>
42
<p>Недурно, 5 строк или 1. Причем выразительность повысилась и такой код проще понимать - один комментарий можно на всякий случай добавить.</p>
42
<p>Недурно, 5 строк или 1. Причем выразительность повысилась и такой код проще понимать - один комментарий можно на всякий случай добавить.</p>
43
<p>В общем виде эта конструкция такова:</p>
43
<p>В общем виде эта конструкция такова:</p>
44
<blockquote><blockquote><blockquote><p>Стоит понимать, что если код совсем не читаем, то лучше отказаться от такой конструкции.</p>
44
<blockquote><blockquote><blockquote><p>Стоит понимать, что если код совсем не читаем, то лучше отказаться от такой конструкции.</p>
45
</blockquote></blockquote></blockquote><h2>Анонимные функции или lambda</h2>
45
</blockquote></blockquote></blockquote><h2>Анонимные функции или lambda</h2>
46
<p>Продолжаем сокращать количества кода.</p>
46
<p>Продолжаем сокращать количества кода.</p>
47
<p>Функция:</p>
47
<p>Функция:</p>
48
<p>Функция короткая, а как минимум 2 строки потратили. Можно ли сократить такие маленькие функции? А может не оформлять в виде функций? Ведь, не всегда хочется плодить лишние функции в модуле. А если функция занимает одну строчку, то и подавно. Поэтому в языках программирования встречаются анонимные функции, которые не имеют названия.</p>
48
<p>Функция короткая, а как минимум 2 строки потратили. Можно ли сократить такие маленькие функции? А может не оформлять в виде функций? Ведь, не всегда хочется плодить лишние функции в модуле. А если функция занимает одну строчку, то и подавно. Поэтому в языках программирования встречаются анонимные функции, которые не имеют названия.</p>
49
<p>Анонимные функции в Python реализуются с помощью лямбда-исчисления и выглядят как лямбда-выражения:</p>
49
<p>Анонимные функции в Python реализуются с помощью лямбда-исчисления и выглядят как лямбда-выражения:</p>
50
<p>Для программиста это такие же функции и с ними можно также работать.</p>
50
<p>Для программиста это такие же функции и с ними можно также работать.</p>
51
<p>Чтобы обращаться к анонимным функциям несколько раз, присваиваем переменной и пользуемся на здоровье.</p>
51
<p>Чтобы обращаться к анонимным функциям несколько раз, присваиваем переменной и пользуемся на здоровье.</p>
52
<p>Пример:</p>
52
<p>Пример:</p>
53
<p>Лямбда-функции могут выступать в качестве аргумента. Даже для других лямбд:</p>
53
<p>Лямбда-функции могут выступать в качестве аргумента. Даже для других лямбд:</p>
54
<h2>Использование lambda</h2>
54
<h2>Использование lambda</h2>
55
<p>Функции без названия научились создавать, а где использовать сейчас узнаем. Стандартная библиотека предоставляет несколько функций, которые могут принимать в качестве аргумента функцию - map(), filter(), reduce(), apply().</p>
55
<p>Функции без названия научились создавать, а где использовать сейчас узнаем. Стандартная библиотека предоставляет несколько функций, которые могут принимать в качестве аргумента функцию - map(), filter(), reduce(), apply().</p>
56
<h3>map()</h3>
56
<h3>map()</h3>
57
<p>Функция map() обрабатывает одну или несколько последовательностей с помощью заданной функции.</p>
57
<p>Функция map() обрабатывает одну или несколько последовательностей с помощью заданной функции.</p>
58
<p>>>> list1 = [7, 2, 3, 10, 12] >>> list2 = [-1, 1, -5, 4, 6] >>> list(map(lambda x, y: x*y, list1, list2)) [-7, 2, -15, 40, 72]</p>
58
<p>>>> list1 = [7, 2, 3, 10, 12] >>> list2 = [-1, 1, -5, 4, 6] >>> list(map(lambda x, y: x*y, list1, list2)) [-7, 2, -15, 40, 72]</p>
59
<p>Мы уже познакомились с генератором списков, давайте им воспользуемся, если длина списков одинаковая:</p>
59
<p>Мы уже познакомились с генератором списков, давайте им воспользуемся, если длина списков одинаковая:</p>
60
<p>Итак, заметно, что использование списковых включений короче, но лямбды более гибкие. Пойдем дальше.</p>
60
<p>Итак, заметно, что использование списковых включений короче, но лямбды более гибкие. Пойдем дальше.</p>
61
<h3>filter()</h3>
61
<h3>filter()</h3>
62
<p>Функция filter() позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:</p>
62
<p>Функция filter() позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:</p>
63
<p>То же самое с помощью списковых выражений:</p>
63
<p>То же самое с помощью списковых выражений:</p>
64
<h3>reduce()</h3>
64
<h3>reduce()</h3>
65
<p>Для организации цепочечных вычислений в списке можно использовать функцию reduce(). Например, произведение элементов списка может быть вычислено так (Python 2):</p>
65
<p>Для организации цепочечных вычислений в списке можно использовать функцию reduce(). Например, произведение элементов списка может быть вычислено так (Python 2):</p>
66
<p>Вычисления происходят в следующем порядке:</p>
66
<p>Вычисления происходят в следующем порядке:</p>
67
<p>Цепочка вызовов связывается с помощью промежуточного результата (res). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):</p>
67
<p>Цепочка вызовов связывается с помощью промежуточного результата (res). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):</p>
68
<p>Разумеется, промежуточный результат необязательно число. Это может быть любой другой тип данных, в том числе и список. Следующий пример показывает реверс списка:</p>
68
<p>Разумеется, промежуточный результат необязательно число. Это может быть любой другой тип данных, в том числе и список. Следующий пример показывает реверс списка:</p>
69
<p>Для наиболее распространенных операций в Python есть встроенные функции:</p>
69
<p>Для наиболее распространенных операций в Python есть встроенные функции:</p>
70
<p>В Python 3 встроенной функции reduce() нет, но её можно найти в модуле functools.</p>
70
<p>В Python 3 встроенной функции reduce() нет, но её можно найти в модуле functools.</p>
71
<h3>apply()</h3>
71
<h3>apply()</h3>
72
<p>Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно (Python 2):</p>
72
<p>Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно (Python 2):</p>
73
<p>В Python 3 вместо функции apply() следует использовать специальный синтаксис:</p>
73
<p>В Python 3 вместо функции apply() следует использовать специальный синтаксис:</p>
74
<p>На этой встроенной функции закончим обзор стандартной библиотеки и перейдем к последнему на сегодня функциональному подходу.</p>
74
<p>На этой встроенной функции закончим обзор стандартной библиотеки и перейдем к последнему на сегодня функциональному подходу.</p>
75
<h2>Замыкания</h2>
75
<h2>Замыкания</h2>
76
<p>Функции, определяемые внутри других функций, представляют собой замыкания. Зачем это нужно? Рассмотрим пример, который объяснит:</p>
76
<p>Функции, определяемые внутри других функций, представляют собой замыкания. Зачем это нужно? Рассмотрим пример, который объяснит:</p>
77
<p>Код (вымышленный):</p>
77
<p>Код (вымышленный):</p>
78
<p>Что можно в коде заметить: в этом коде переменные, которые живут по сути постоянно (т.е. одинаковые), но при этом мы загружаем или инициализируем по несколько раз. В итоге приходит понимание, что инициализация переменной занимает львиную долю времени в этом процессе, бывает что даже загрузка переменных в scope уменьшает производительность. Чтобы уменьшить накладные расходы необходимо использовать замыкания.</p>
78
<p>Что можно в коде заметить: в этом коде переменные, которые живут по сути постоянно (т.е. одинаковые), но при этом мы загружаем или инициализируем по несколько раз. В итоге приходит понимание, что инициализация переменной занимает львиную долю времени в этом процессе, бывает что даже загрузка переменных в scope уменьшает производительность. Чтобы уменьшить накладные расходы необходимо использовать замыкания.</p>
79
<p>В замыкании однажды инициализируются переменные, которые затем без накладных расходов можно использовать.</p>
79
<p>В замыкании однажды инициализируются переменные, которые затем без накладных расходов можно использовать.</p>
80
<p>Научимся оформлять замыкания:</p>
80
<p>Научимся оформлять замыкания:</p>
81
<h2>Заключение</h2>
81
<h2>Заключение</h2>
82
<p>В уроке мы рассмотрели базовые понятия ФП, а также составили список механизмов, которые будут рассмотрены в следующих уроках. Поговорили о способах уменьшения количества кода, таких как cписковые включения (генератор списка), lamda функции и их использовании и на последок было несколько слов про замыкания и для чего они нужны.</p>
82
<p>В уроке мы рассмотрели базовые понятия ФП, а также составили список механизмов, которые будут рассмотрены в следующих уроках. Поговорили о способах уменьшения количества кода, таких как cписковые включения (генератор списка), lamda функции и их использовании и на последок было несколько слов про замыкания и для чего они нужны.</p>