Привет, Хабр!
В этой статье я хотел бы рассказать о том, что пришло в Python из функциональных языков программирования. Заинтересовавшихся прошу под кат.


Генераторы списков


Это легко и просто: вместо


l = []
for x in range(10):
    if x % 2 == 0:
        l.append(x)

мы пишем


l = [x for x in range(10) if x % 2 == 0]

Коротко и понятно.


Как это работает в функциональных языках

В Haskell тоже самое буде выглядеть так:


let l = [x | x <- [0..10], x `mod` 2 == 0]

Лямбды


Допустим, мы пишем графический интерфейс и у нас есть функция button(**kwargs), где допустимые именованные аргументы: text — для текста, width — для ширины, height — для высоты и command — для callback-функции:


def callback(event):
    print("Button pressed")
button(text="Press me", width=32, height=16, command=callback)

Обратите внимание, насколько маленький у нас callback, неужели его нелзя пропихнуть аргументом? Можно! Нам помогут лямбды:


button(text="Press me", width=32, height=16, command=lambda x: print("Button pressed"))

Чисто и легко!


Как это работает в функциональных языках

В Haskell передача функции в качестве аргумента встречается на каждом шагу, например функция map берет функцию и список и возвращает список, к каждому элементу которого была применена эта функция:


map (\x -> x+1) [1..10]

На Python это:


map(lambda x: x+1, [x for x in range(1, 10)])

Правда, в Python нет map.
upd: map есть!


Карринг


Карринг (каррирование) — это когда мы передаем старой функции один или несколько аргументов, чтобы получить новую, которая принимает остальные (спасибо AnutaU за более точное определение). Например: print — это функция (я использую Python 3), у нее есть именованный аргумент end — конец строки, по умолчанию он равен "\n". Я хочу не переходить на новую строку, тогда пишу


print(str, end="")

Давайте сделаем функцию printf, которая не будет переходить на новую строку


def printf(*args, **kwargs):
    kwargs["end"] = ""
    print(*args, **kwargs)

Коряво, можно и проще:


from functools import partial

printf = partial(print, end = "")

Вот он, карринг — мы говорим, что хотим точно такой же, но с перламутровыми пуговицами функцию print, но чтобы end был равен "". Все просто.


Как это работает в функциональных языках

И снова Haskell: у нас есть функция +, которая берет два аргумента, мы говорим:


let plusTwo = (+2)

теперь у меня есть функция, которая прибавляет 2 к единственному аргументу.


У меня все, если вы знаете, что еще есть в Python из функциональщины — прошу в комментарии.
Вопросы и отзывы туда же.

Комментарии (13)


  1. AnutaU
    22.08.2017 15:49
    +3

    Правда, в Python нет map.
    Как же это нет?


  1. AnutaU
    22.08.2017 15:57
    -1

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

    Во многих функциональных языках вообще не существует функций нескольких аргументов, они эмулируются либо с помощью каррирования, либо с помощью кортежей.


    1. s2002kir Автор
      22.08.2017 16:00

      Я действительно кривовато объяснил, а насчёт функциональных языков — да так и есть.


  1. playermet
    22.08.2017 16:22
    +6

    То что в статье названо каррированием, на самом деле является частичным применением.


  1. koldyr
    22.08.2017 16:25
    +1

    Третий случай — это частичное применение.
    Каррирование имеет сигнатуру
    curry :: ((a,b)->c)->a->b->c


  1. saw_tooth
    22.08.2017 17:28
    +3

    А где filter?map?reduce?functool?
    Если это обзорная статья, то нужно говорить о всех вещах, а не только об очевидном.
    Низачет, в общем то…


  1. Kwent
    22.08.2017 17:40

    Официальная дока и вот эта милая книжка (мне понравилась). А еще на хабре немного было.


  1. kesn
    22.08.2017 20:02

    Ваше «чисто и легко» просто не сработает! stackoverflow.com/questions/2970858/why-doesnt-print-work-in-a-lambda


    1. s2002kir Автор
      22.08.2017 20:21

      Вот здесь вы не правы: во-первых, я сказал, что использую Python 3, во-вторых, по скобочкам видно, что это функция — т. е. то, что нужно


      1. kesn
        22.08.2017 20:40
        +1

        да, вы правы — перепутал со вторым питоном :(


  1. domix32
    24.08.2017 11:18

    Стоит однако отметить что генераторы списков в Python 2 и 3 называются иначе. В Python 2.7 range возвращает массив, а не генератор, а для генератора необходимо вызывать xrange. В третьей версии xrange стал просто range, а выделение готовых массивов происходит через другие механизмы.


    1. s2002kir Автор
      24.08.2017 14:30
      +1

      Я же сказал, что использую Python 3


      1. domix32
        24.08.2017 21:40
        -2

        В статье — только тэг. И никаких деталей