20230930204604 #учу/ФП
- "Функцiональне програмування" - "Функциональное программирование" (ФП)
- [[ФП - Лекция 11]]
- --> [[ФП - UA - Лекцiя 12]]
- <-- [[ФП - UA - Лекцiя 10]]
- Джерело https://habr.com/ru/post/555378/"
- Операції відображення та фільтрації -> lambda, map, filter, reduce, zip, enumerate - див [[ФП - Лекція 10]]
- Звести список чисел у квадрат можна так:
squared_numbers = [x*x for x in numbers]
- Эксперименты
>>> squared_numbers = [x*x for x in (1,2,3,4,5)]
>>> squared_numbers
[1, 4, 9, 16, 25]
>>> squared_numbers
[1, 4, 9, 16, 25]
>>> squared_numbers = [x*x for x in [1,2,3,4,5]]
>>> squared_numbers
[1, 4, 9, 16, 25]
>>> squared_numbers
[1, 4, 9, 16, 25]
>>> squared_numbers = [x*x for x in squared_numbers]
>>> squared_numbers
[1, 16, 81, 256, 625]
>>> squared_numbers
[1, 16, 81, 256, 625]
>>>
- Python підтримує концепцію «включення до послідовності» ( описом послідовності). Загальний вигляд:
[выражение for переменная in список if выражение2]
- В даному загальному форматі вираз - це вираз або функція за участю
- змінна повертають значення - елемент послідовності.
- список - оброблюваний список
- вираз 2 - логічний вираз або предикативна функція за участю змінної.
- Попередній приклад.
numbers
задані окремим об'єктом.
>>> numbers = [1, 2, 3, 4, 5]
>>> squared_numbers = [x*x for x in numbers]
>>> squared_numbers
[1, 4, 9, 16, 25]
- Еквівалентно наступному фрагменту програмного коду:
>>> squared_numbers = []
>>> for x in numbers:
>>> squared_numbers.append(x*x)
>>> squared_numbers
[1, 4, 9, 16, 25]
- Інтенсіонал. Така форма - додана синтаксична конструкція, що дозволяє записувати вирази у простіших формах. Легко читаються звичайною мовою. Код стає зрозумілішим. Описує безліч шляхом визначення умови, що має виконуватися всім його членів.
- Приклади форми опису інтенсіоналу
[x*x for x in numbers]
- опис списку{x:x*x for x in numbers}
- опис словника{x*x for x in numbers}
;set(x*x for x in numbers)
- опис множини(x*x for x in numbers)
- опис послідовності, що ітерується. Така форма запису створює генератор послідовності. Генератор - це об'єкт, який можна послідовно обійти (зазвичай за допомогою інструкціїfor
), але чиї значення надаються тільки тоді, коли вони потрібні, використовуючи ліниве обчислення.
- За винятком опису словника, відрізняються лише обмежуючими символами (дужками)
- Т.ч., приклади з розділів про функції
map
таfilter
[[ФП - Лекція 10]] можна переписати з використанням включення до послідовності.
>>> seq = (1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> seq2 = (5, 6, 7, 8, 9, 0, 3, 2, 1)
>>> result = [x + y for x, y in zip(seq, seq2)]
>>> result
[6, 8, 10, 12, 14, 6, 10, 10, 10]
- Квадратні дужки у визначенні (рядок 3) – у результаті цієї операції буде створено список.
- Функція
zip
- поєднує відповідні елементи кожної послідовності у двоелементні кортежі. Якби послідовностей було три, то вони поєднувалися б у кортежі з трьох елементів і т.д. - Експеримент
>>> seq1 = (1,2,3)
>>> seq2 = (4,5,6)
>>> seq3 = (7,8,9)
>>> result = [x+y+z for x,y,z in zip(seq1,seq2,seq3)]
>>> result
[12, 15, 18]
>>> result
[12, 15, 18]
- Включення до списку – замість функції
filter
:
>>> result = [x for x in seq if is_even(x)]
>>> result
[2, 4, 6, 8]
- Квадратні дужки – створено список.
- Спосіб обробки послідовностей
- з використанням функцій вищого порядку або
- включень,
- Предмет особистих переваг.
- Функції вищого порядку не тільки отримують функції на вході, а й можуть породжувати нові функції на виході. Вони навіть можуть запам'ятовувати посилання на значення у функції, яку вони генерують. Це називається замиканням. Функція, що має замикання, може «запам'ятовувати» та отримувати доступ до середовища вкладених у нього значень.
- Використовуючи замикання, можна розділити виконання функції з багатьма аргументами на більшу кількість кроків. Каррування - Хаскел Каррінг.
- Карірованіе - це перетворення функції багатьох аргументів на функцію, що бере свої аргументи по одному. Наприклад, припустимо, ваш програмний код має наведену нижче стандартну функцію
adder
:
def adder(n, m):
return n + m
- Каррована - переписати:
def adder(n):
def fn(m):
return n + m
return fn
- Це ж саме – за допомогою лямбда-функцій:
adder = lambda n: lambda m: n + m
- Використовуються дві вкладені лямбда-функції, кожна з яких приймає лише один аргумент.
- У такому записі функція
adder
тепер може викликатися всього з одним аргументом. - Вираз
adder(3)
повертає не число, а нову, карровану функцію. Під час виклику функціїadder
зі значенням 3 як перший аргумент посилання на значення 3 запам'ятовується в карірованій функції. А далі відбувається таке:
>>> sum_three = adder(3)
>>> sum_three
<function __main__.<lambda>.<locals>.<lambda>>
>>> sum_three(1)
4
- Каррова функція
adder(3)
присвоюється змінноюsum_three
, яка тепер на неї посилається. Якщо викликати функціюsum_three
, передавши їй другий аргумент, вона поверне результат складання двох аргументів 3 і 1. - Експеримент:
>>> sum3 =adder(3)
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
sum3 =adder(3)
NameError: name 'adder' is not defined
>>> adder=lambda n:lambda m:lambda k: n+m+k
>>> sum3 = adder(1)
>>> sum3
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
>>> sum3 (2)
<function <lambda>.<locals>.<lambda>.<locals>.<lambda> at 0x0000020179483DC0>
>>> sum3 (3)
<function <lambda>.<locals>.<lambda>.<locals>.<lambda> at 0x0000020179483E50>
>>> sum3
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
>>> print sum3
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(sum3)?
>>> print(sum3)
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
>>> list(sum3)
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
list(sum3)
TypeError: 'function' object is not iterable
>>> sum3
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
>>> print(*sum3)
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
print(*sum3)
TypeError: print() argument after * must be an iterable, not function
>>> sum3
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
>>> qq = sum3
>>> print (qq)
<function <lambda>.<locals>.<lambda> at 0x0000020179483D30>
- Замикання також використовуються для генерування набору пов'язаних функцій за шаблоном. Використання шаблону функції допомагає зробити програмний код більш читаним та уникати дублювання. Приклад:
def power_generator(base):
return lambda x: pow(x, base)
- Функція power_generator може застосовуватися для створення різних функцій, які обчислюють ступінь:
>>> square = power_generator(2) # функция возведения в квадрат
>>> square(2)
4
>>> cube = power_generator(3) # функция возведения в куб
>>> cube(2)
8
- Зазначимо, що функції
square
таcube
зберігають значення змінноїbase
. Ця змінна існувала лише у середовищіpower_generator
, незважаючи на те, що ці повернені функції абсолютно незалежні від функціїpower_generator
.- Замикання - це функція, яка має доступ до деяких змінних за межами свого власного середовища. !!!
- Замикання також можуть використовуватися для управління внутрішнім станом функції. Припустімо, що потрібна функція, яка накопичує суму всіх чисел, які їй надаються. Один із способів це зробити полягає у використанні глобальної змінної:
COUNT = 0
def count_add(x):
global COUNT
COUNT += x
return COUNT
- Застосування глобальних змінних слід уникати, тому що вони забруднюють простір імен програми. Чистіший підхід полягає у використанні замикання, щоб включити посилання на змінну змінну:
def make_adder():
n = 0
def fn(x):
nonlocal n
n += x
return n
return fn
- Такий підхід дозволяє створювати кілька лічильників без застосування глобальних змінних. Використано ключове слово
nonlocal
, яке оголошує, що зміннаn
не є локальною для вкладеної функціїfn
. - Приклад роботи:
>>> my_adder = make_adder()
>>> print(my_adder(5)) # напечатает 5
>>> print(my_adder(2)) # напечатает 7 (5 + 2)
>>> print(my_adder(3)) # напечатает 10 (5 + 2 + 3)
- Деякі мови програмування строго функціональні. Haskell. Весь код еквівалентний чистим математичним функціям. Ці мови заходять настільки далеко, що є позачасовими, причому порядок операторів у програмному коді не втручається у поведінку коду. У цих мовах всі надані змінним значення є немутованими. Таке присвоєння називається одноразовим. Оскільки стан програми відсутня, то немає часу, коли змінна може змінитися. Обчислення в суворій функціональній парадигмі легко зводяться до обчислення функцій і зіставлення з шаблонами.
- --> [[ФП - UA - Лекцiя 12]]
- <-- [[ФП - UA - Лекцiя 10]]