Skip to content

Latest commit

 

History

History
249 lines (213 loc) · 13.2 KB

ФП - UA - Лекцiя 11.md

File metadata and controls

249 lines (213 loc) · 13.2 KB

20230930204604 #учу/ФП


  • "Функцiональне програмування" - "Функциональное программирование" (ФП)
  • [[ФП - Лекция 11]]
  • --> [[ФП - UA - Лекцiя 12]]
  • <-- [[ФП - UA - Лекцiя 10]]

Тема: "Функціональне програмування на Python - 4"

Включення до послідовності

  • Операції відображення та фільтрації -> 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]]