HTML Diff
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>&gt;&gt;&gt; list1 = [7, 2, 3, 10, 12] &gt;&gt;&gt; list2 = [-1, 1, -5, 4, 6] &gt;&gt;&gt; list(map(lambda x, y: x*y, list1, list2)) [-7, 2, -15, 40, 72]</p>
58 <p>&gt;&gt;&gt; list1 = [7, 2, 3, 10, 12] &gt;&gt;&gt; list2 = [-1, 1, -5, 4, 6] &gt;&gt;&gt; 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>