📦 Репозиторий занятия 42

Урок 42. Lambda-функции. Функции высшего порядка

Как работать с репозиторием

Каждая карточка говорит, о чём файл, что он выводит и что в нём искать. Код виден прямо здесь: его можно скопировать одной кнопкой, скачать файл или открыть его целиком.

Маршрут изучения

  1. Прочитайте описание: по нему уже понятно, о чём файл и что он выведет.
  2. Предскажите вывод: сравните своё предположение со строкой «Что выводит».
  3. Запустите: скачайте файл или скопируйте код кнопкой и выполните его у себя.
  4. Измените: поменяйте одно условие или значение и объясните новый результат.

Файлы: рекомендуемый порядок

1
MarkdownРазбор концепции79 строк

Функции первого класса и функции высшего порядка

less_22__lambda__prog_paradigm__map_filter_reduce/theory_01__first-class_and_higher-order_functions.md

Объясняет, почему функция в Python — полноценный объект: её можно присвоить переменной, передать аргументом, вернуть из другой функции и положить в список или словарь. Вторая половина файла разделяет два похожих термина: функция первого класса — это свойство языка, функция высшего порядка — конкретная функция, которая принимает или возвращает другую функцию.

  • add(a, b) присваивается переменной f и вызывается как f(2, 3)
  • caller(func, x, y) — функция, принимающая функцию аргументом
  • funcs = [add, multy] — хранение функций в списке
  • apply(square, 5) — пример «принимает функцию»
  • make_multiplier(n) — пример «возвращает функцию»
Показать начало файла (79 строк всего)
# Функции первого класса и высшего порядка Python


Это базовое понятие функционального программирования. 


### Функции первого класса (first-class functions)

Это функция, которая в языке программирования является ПОЛНОЦЕННЫМ объектом.

Иными словами, её можно:

1. **Присваивать переменной**
2. **Передавать как аргумент другой функции**
3. **Возвращать из другой функции**
4. **Хранить в структурах данных** (списки, словари и т. д.)


```python
def add(a, b):
    return a + b

f = add            # присвоение функции переменной
print(f(2, 3))     # 5

def caller(func, x, y):
    return func(x, y)

print(caller(add, 4, 5))  # 9


def multy(x, y):
    return x * y

funcs = [add, multy]  # хранение функций в списке
print(funcs[1](3, 4))             # 12
```

---

### Функции высшего порядка (higher-order functions)

Это функция, которая:

1. **Принимает другую функцию как аргумент** и/или
2. **Возвращает функцию как результат**


#### Пример 1 — функция принимает функцию как аргумент:

```python
def square(x):
    return x * x


def apply(func, x):
    return func(x)


print(apply(square, 5))  # 25
…
Проверьте себя: Чем функция первого класса отличается от функции высшего порядка — и может ли одна и та же функция быть и тем и другим?
2
MarkdownРазбор концепции68 строк

Lambda-функция: синтаксис, ограничения и сортировка

less_22__lambda__prog_paradigm__map_filter_reduce/theory_02__lambda_function.md

Определяет lambda как синтаксический сахар над обычной функцией: одно выражение, без имени, без многострочного тела. Отдельно отмечено требование PEP 8 не давать lambda имя — она нужна как callback. Практическая часть: сортировка словаря по ключу и по значению и сортировка списка кортежей сразу по двум критериям через key=lambda x: (x[0], -x[1]).

  • def f1(x) и f2 = lambda x — сравнение записей
  • Чего lambda не может: несколько строк и пустой возврат
  • sorted(d.items(), key=lambda x: x[0]) — по ключу
  • sorted(d.items(), key=lambda x: -x[1]) — по значению убыв.
  • sorted(data, key=lambda x: (x[0], -x[1])) — два критерия
  • lambda с несколькими аргументами: lambda x, y: x + y
Показать начало файла (68 строк всего)
# Лямбда функция


## Определение

Другие неофициальные названия:
* Анонимная функция (т.е. функция без имени)
* Однострочная функция (содержащая ТОЛЬКО одну строку)

Это синтаксический сахар обычной функции.

```python
def f1(x):
    return x * x


f2 = lambda x: x * x
```

Лямбда функции НЕ может:
* ничего не возвращать
* быть больше одной строки

Правила PEP 8 категорически не рекомендуют создавать именные функции с помощью lambda:

```python
f2 = lambda x: x * x
```

Обычно lambda-функции используются ТОЛЬКО в качестве callback-функций.

#### Пример 1: сортировка словаря по одному параметру:

```python
d = {'c': 5, 'b': 1, 'a': 3}

# сортировка по ключу по возрастанию:
print(dict(sorted(d.items(), key=lambda x: x[0])))
# {'a': 3, 'b': 1, 'c': 5}

# сортировка по значению по убыванию:
print(dict(sorted(d.items(), key=lambda x: -x[1])))
# {'c': 5, 'a': 3, 'b': 1}
```

#### Пример 2: сортировка по двум параметрам:

```python
data = [
    ("apple", 5),
    ("banana", 2),
    ("apple", 8),
    ("banana", 7),
    ("cherry", 1),
]

sorted_data = sorted(data, key=lambda x: (x[0], -x[1]))
print(sorted_data)
# [('apple', 8), ('apple', 5), ('banana', 7), ('banana', 2), ('cherry', 1)]
```
…
Проверьте себя: Почему в key=lambda x: (x[0], -x[1]) минус стоит только у второго поля и что изменится от reverse=True?
3
MarkdownРазбор концепции167 строк

Парадигмы программирования и чистые функции

less_22__lambda__prog_paradigm__map_filter_reduce/theory_03__programming_paradigms.md

Обзор пяти парадигм — императивной, декларативной, ООП, функциональной и логической — с примером кода на каждую. Ключевая для урока часть — функциональная парадигма: разбор чистой функции по двум признакам (детерминированность и отсутствие побочных эффектов), пример нечистой функции, зависящей от глобальной переменной, и объяснение, почему чистые функции проще тестировать и безопасно выполнять параллельно.

  • Императивная: цикл суммирования (КАК)
  • Декларативная: SELECT SUM(number) (ЧТО)
  • ООП: класс Car с методом drive()
  • Функциональная: reduce(lambda x, y: x + y, numbers)
  • Чистая функция add(a, b) против нечистой increment_by_x(y)
  • Логическая: правила предок/родитель на Prolog
Показать начало файла (167 строк всего)
# Парадигмы программирования

Это основная концепция или стиль разработки программного обеспечения,  
который определяет подход к решению задач с помощью программирования. 

Это, своего рода, набор принципов и методов, которые определяют, 
- как именно строится и организуется код, 
- как мы думаем о проблемах и решениях в контексте разработки программ.

Парадигма программирования влияет на 
- структуру программы, 
- способы обработки данных, 
- управление состоянием 
- и взаимодействие компонентов. 

- В разных парадигмах ставятся разные акценты на эти аспекты, что и определяет их особенности.


## 1. Императивное программирование

### Описание:
Императивное программирование основано на пошаговом описании того, как должна выполняться задача. 
Программист задаёт последовательность команд, изменяющих состояние программы. (КАК, а не ЧТО) 

### Пример языков:
C, Python, Java, Pascal

### Пример кода (Python):
```
summa = 0
for i in range(1, 6):
    summa += i
print(summa)  # Вывод: 15
```
Здесь мы явно указываем, как именно вычислить сумму чисел.

## 2. Декларативное программирование

### Описание:
Декларативный подход описывает ЧТО нужно сделать, а не КАК. 
Управление выполнением берёт на себя язык или среда выполнения.

### Пример языков:
SQL, HTML, CSS

### Пример кода (SQL):
```
SELECT SUM(number) FROM db_name.numbers;
```
Здесь мы просто говорим, что нужно найти сумму чисел, не указывая, каким алгоритмом это сделать.

## 3. Объектно-ориентированное программирование (ООП)

### Описание:
ООП основано на концепции объектов, которые объединяют данные (свойства) и поведение (методы). 
Основные принципы: инкапсуляция, наследование, полиморфизм.

### Пример языков:
Java, Python, C++, C#

…
Проверьте себя: Функция читает время через datetime.now() и возвращает строку — чистая она или нет, и почему?
4
PythonИсполняемый пример13 строк

all() и any(): все элементы или хотя бы один

less_22__lambda__prog_paradigm__map_filter_reduce/theory_04__all_and_any.py

Два коротких примера с аннотациями типов: all_more_than_value() отвечает «все ли элементы больше порога», at_least_one_is_bigger() — «есть ли хотя бы один больше порога». Обе функции строятся на генераторном выражении внутри вызова: обходить список циклом и заводить флаг не нужно.

  • all_more_than_value(nums, value) — all(n > value for n in nums)
  • at_least_one_is_bigger(nums, value) — any(n > value for n in nums)
  • Аннотации типов: list[int | float] и int | float
  • Проверка на списке [2, 9, 4, 1, 12]

Что выводит: True и True — все элементы больше 0, и хотя бы один больше 11 (это 12).

Файл целиком (13 строк)
def all_more_than_value(nums: list[int | float], value: int | float):
    """Все ли без исключения элементы списка больше value?"""
    return all(n > value for n in nums)


def at_least_one_is_bigger(nums: list[int | float], value: int | float):
    """Хотя бы один элемент больше value?"""
    return any(n > value for n in nums)


lst = [2, 9, 4, 1, 12]
print(all_more_than_value(lst, 0))      # True
print(at_least_one_is_bigger(lst, 11))  # True
Проверьте себя: Что вернут обе функции, если передать пустой список, и почему ответы будут разными?
Открыть файл →
5
PythonИсполняемый пример38 строк

map, filter, reduce на одном наборе чисел

less_22__lambda__prog_paradigm__map_filter_reduce/theory_05__map_filter_reduce.py

Главный исполняемый файл урока. Показывает, что map и filter возвращают итератор (isinstance(..., Iterator) даёт True), а не список, и что распаковка print(*obj) исчерпывает его — второй print печатает пустую строку. Отдельно демонстрируется map по двум последовательностям сразу и reduce с начальным значением: в комментариях к файлу разобрана сигнатура reduce(function, sequence[, initial]) и правило «без initial аккумулятором становится первый элемент».

  • map(str, nums) — объект map, проверка isinstance(..., Iterator)
  • map(lambda x, y: x + y, nums, nums) — две последовательности
  • filter(lambda x: x % 2 == 0, nums) — только чётные
  • Комментарий-справка по сигнатуре reduce(function, sequence[, initial])
  • reduce(lambda acc, x: acc + x, nums, 10) — свёртка с initial
  • reduce(lambda acc, x: acc + x, [10, 0, 0]) — свёртка без initial

Что выводит: Сначала <map object ...> и True, затем 1 2 3 4 5 6 7 8 и пустая строка от исчерпанного итератора; далее 2 4 6 8 10 12 14 16, <filter object ...>, True, 2 4 6 8, и в конце 46 и 10.

Начало файла
from functools import reduce
from typing import Iterator

nums = [1, 2, 3, 4, 5, 6, 7, 8]

""" =========================== map =========================== """
map_object = map(str, nums)
print(map_object)
print(isinstance(map_object, Iterator))
print(*map_object)
print()

map_object_2 = map(lambda x, y: x + y, nums, nums)
print(*map_object_2)
Показать файл целиком (38 строк)
from functools import reduce
from typing import Iterator

nums = [1, 2, 3, 4, 5, 6, 7, 8]

""" =========================== map =========================== """
map_object = map(str, nums)
print(map_object)
print(isinstance(map_object, Iterator))
print(*map_object)
print()

map_object_2 = map(lambda x, y: x + y, nums, nums)
print(*map_object_2)
print()

""" =========================== filter =========================== """
filter_obj = filter(lambda x: x % 2 == 0, nums)
print(filter_obj)
print(isinstance(filter_obj, Iterator))
print(*filter_obj)
print()

""" =========================== reduce =========================== 
reduce(function, sequence[, initial])
    function — функция, принимающая два аргумента (аккумулятор и текущий элемент).
    iterable — последовательность элементов.
    initial (необязательный) — начальное значение аккумулятора.
По умолчанию initial равен отсутствию значения.
Если initial не указан, он становится равным первому элементу.
После этого reduce() начинает применять функцию, начиная со второго элемента.
(сделайте CTRL + клик по функции reduce и посмотрите код самой reduce)
"""

reduce_obj = reduce(lambda acc, x: acc + x, nums, 10)
print(reduce_obj)  # 46
reduce_obj2 = reduce(lambda acc, x: acc + x, [10, 0, 0])
print(reduce_obj2)  # 10
Проверьте себя: Почему второй print(*map_object) ничего не печатает и что нужно сделать, чтобы пройти по результату дважды?
Открыть файл →