Функции высшего порядка python. Введение в функциональное программирование на Python

03.04.2019

Порядок выполнения операций при вычислении значения выражения определяется расположением знаков операций , круглых скобок и приоритетом операций . Операции с наивысшим приоритетом выполняются в первую очередь. Если в выражении содержится несколько операций одного приоритета на одном и том же уровне, то их обработка производится в соответствии с порядком выполнения – справа налево или слева направо. Если необходимо изменить порядок выполнения операций в выражении, то следует использовать круглые скобки, например (x + y) * z.

Приоритет операции запятая ниже, чем у всех остальных операций.

В приведенной ниже таблице операции языка C++ приведены в порядке убывания приоритета. Операции с разными приоритетами разделены чертой.

Таблица приоритетов операций

Знаки операций

Названия операций

Порядок выполнения

повышение приоритета

постфиксный инкремент

постфиксный декремент

слева направо

sizeof

(тип) выражение и

тип (выражение)

размер операнда в байтах

префиксный инкремент

префиксный декремент

поразрядное Н Е

логическое НЕ

унарные минус, плюс

преобразование типа

справа налево

умножение

остаток от деления целых

слева направо

сложение

вычитание

слева направо

сдвиг влево

сдвиг вправо

слева направо

меньше или равно

больше или равно

слева направо

слева направо

поразрядное И

слева направо

поразрядное исключающее ИЛИ

слева направо

поразрядное ИЛИ

слева направо

логическое И

слева направо

логическое ИЛИ

слева направо

? :

условная

справа налево

*= , /= , %=

+= , - =

<<= , >>=

&= , |= , ^=

присваивание (простое и

составное)

справа налево

операция запятая

слева направо

Приведение (преобразование) типа

Язык программирования С++ , являясь типизированным языком, позволяет тем не менее весьма свободно обращаться с выражениями, оперирующими различными типами данных. В этом случае операнды выражения приводятся к некоторому общему типу.

Автоматически производятся лишь преобразования, которые преобразуют операнды с меньшим диапазоном значений в операнды с большим диапазоном значений, поскольку это происходит без какой-либо потери информации. Например, если в выражении ival + f v al переменная ival типа int , а переменная f v al – типа float , то при выполнении операции (+ ) значение переменной iv al будет приведено к типу float .

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

Для любого выражения можно явно указать преобразование его типа, используя унарную операцию, называемую приведением (преобразованием) типа . Операция может быть записана в двух форматах:

(тип ) выражение

тип (выражение)

Операндом операции приведения типа является преобразуемое выражение. Приоритет операции приведения типа такой же, как и у других унарных операций. Например: (long double ) 5; (int ) f; (double) a/2.

Если рассматриваемое выражение имеет достаточно сложный вид, желательно брать его в скобки, чтобы убедится, что тип будет изменен у результата всего выражения, а не у его части. Например,

(int ) x + b * c

(int ) (x + b * c )

В первом случае преобразование относится к переменной x , во втором – ко всему выражению x + b * c .

2010-11-17 09:47

Функции map, zip и лямбда (кстати говоря называются "функции высшего порядка" или "first-class-functions") позволяют достаточно просто выполнять различные манипуляции с данными, для чего в "обычном" процедурном стиле приходится писать немного больше кода. Все ниженаписанное относится к так называемому функциональному программированию , луркайте подробности.

Функции map, zip и lambda в примерах.

Простая задача есть список a = и список b = одинаковой длины и нужно слить их парами. Проще простого - используя функцию zip :

a = [ 1 , 2 ] b = [ 3 , 4 ] print zip (a , b ) [(1 , 3 ), (2 , 4 )]

или тройками:

a = [ 1 , 2 ] b = [ 3 , 4 ] c = [ 5 , 6 ] print zip (a , b , c ) [(1 , 3 , 5 ), (2 , 4 , 6 )]

или в более общем виде

list = [ a , b , c ] print zip (* list ) [(1 , 3 , 5 ), (2 , 4 , 6 )]

Звездочка * перед list как-бы говорит что передается список аргументов, т.е. Действовать эквивалентно тому как если бы передали a, b, c т.е. Можно даже так print zip(*) результат не изменится.

def f (x ): return x * x nums = [ 1 , 2 , 3 ] for num in nums : print f (num )

Более опытный нуб изучивший list comprehensions:

def f (x ): return x * x print [ f (num ) for num in nums ]

Программист сделает проще:

def f (x ): return x * x print map (f , nums )

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

print map (lambda x : x * x , nums )

Последняя запись являет собой пример наиболее грамотного подхода. Дело в том, что когда человек пишет код как стихи, в порыве вдохновения (что другими словами можно назвать "в диком угаре"), крайне роляет скорость написания (отсюда растут корни трепетной любви многих девелоперов к простым текстовым редакторм vim, emacs, sublimetext), а сильная сторона питона как раз в размере генерируемого кода - он очень компактный. Написать одну строчку естественно быстрее чем 7, да и читать короткий код проще, однако написание подобного кода требует определенного навыка. Другая сторона медали – иногда в этом "диком угаре" пишут в одну строчку целые последовательности достаточно сложных действий, да так что очень трудно понять что там происходит и что получается в конечном итоге.

Из примера понятно, что map применяет какую-либо функцию к списку и возвращает результат опять же в виде списка. Вы можете передать несколько списков, тогда функция (идущая первым параметром) должна принимать несколько аргументов (по количеству списков переданных в map ).

def f (x , y ): return x * y a = [ 1 , 3 , 4 ] b = [ 3 , 4 , 5 ] print map (f , a , b ) [ 3 , 12 , 20 ]

Классно, правда?

Однако если списки разной длины, т.е. Один короче другого, то он будет дополнен значениями None до нужной длины. Если убрать из списка b последнее значение – пример не будет работать, т.к. В функции f произойдет попытка умножения числа на None, и питоне не позволяет это делать, что кстати выгодно отличает его от php, который в подобной ситуации работал бы дальше. Поэтому если функция f достаточно объемна, неплохо бы проверять передаваемые значения. Например;

Если же заместо функции стоит None – то map действует примерно так же как и zip , но если передаваемые списки разной длины в результат будет писаться None – что кстати очень уместно в некоторых моментах.

a = [ 1 , 3 , 4 ] b = [ 3 , 4 ] print map (None , a , b ) [(1 , 3 ), (3 , 4 ), (4 , None )]

Теперь про лямбда функции в python . Они используются когда вам необходимо определить функцию без исподьзования def func_name(): ..., ведь часто (как в предыдущих примерах) функция настолько мала, что определять её отдельно смыла нет (лишние строчки кода, что ухудшение читабельность). Поэтому функцию можно определить “на месте” f = lambda x: x*x как бы говорит нам – принимает x, возвращает x*x

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

def f (x , y ): if (y == None ): y = 1 return x * y

можно представить как:

lambda x , y : x * (y if y is not None else 1 )

А теперь хорошо бы передавать списки отсортированные по длине – len(a) > (b) – проще простого - воспользуемся функцией sorted :

sorted ([ a , b ], key = lambda x : len (x ), reverse = True )

фунция sorted принимает список значений ( = [,]) и сортирует по ключу key – который у нас задан функцией len(x) - возвращающей длину списка, сортируем в порядке убывания (reverse=True)

В конечном итоге вся операция записывается таким образом:

map (lambda x , y : x * (y if y is not None else 1 ), * sorted ([ a , b ], key = lambda x : len (x ), reverse = True ))

списки a и b могут быть разной длины и передаваться в каком угодно порядке. Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.

Существует несколько парадигм в программировании, например, ООП, функциональная, императивная, логическая, да много их. Мы будем говорить про функциональное программирование.

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

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

Теория в теории

Как и в разговоре об ООП, так и о функциональном программировании, мы стараемся избегать определений. Все-таки четкое определение дать тяжело, поэтому здесь четкого определения не будет. Однако! Хотелки для функционального языка выделим:

  • Функции высшего порядка
  • Чистые функции
  • Иммутабельные данные

Это не полный список, но даже этого хватает чтобы сделать сделать "красиво". Если читателю хочется больше, то вот расширенный список:

  • Функции высшего порядка
  • Чистые функции
  • Иммутабельные данные
  • Замыкания
  • Ленивость
  • Хвостовая рекурсия
  • Алгебраические типы данных
  • Pattern matching

Постепенно рассмотрим все эти моменты и как использовать в Python.

А сегодня кратко, что есть что в первом списке.

Чистые функции

Чистые функции не производят никаких наблюдаемых побочных эффектов, только возвращают результат. Не меняют глобальных переменных, ничего никуда не посылают и не печатают, не трогают объектов, и так далее. Принимают данные, что-то вычисляют, учитывая только аргументы, и возвращают новые данные.

  • Легче читать и понимать код
  • Легче тестировать (не надо создавать «условий»)
  • Надежнее, потому что не зависят от «погоды» и состояния окружения, только от аргументов
  • Можно запускать параллельно, можно кешировать результат

Иммутабельные данные

Иммутабельные структуры данных - это коллекции, которые нельзя изменить. Примерно как числа. Число просто есть, его нельзя поменять. Также и иммутабельный массив - он такой, каким его создали, и всегда таким будет. Если нужно добавить элемент - придется создать новый массив.

Преимущества неизменяемых структур:

  • Безопасно разделять ссылку между потоками
  • Легко тестировать
  • Легко отследить жизненный цикл (соответствует data flow)

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

Функцию, принимающую другую функцию в качестве аргумента и/или возвращающую другую функцию, называют функцией высшего порядка :

Def f(x): return x + 3 def g(function, x): return function(x) * function(x) print(g(f, 7))

Рассмотрели теорию, начнем переходить к практике, от простого к сложному.

Списковые включения или генератор списка

Рассмотрим одну конструкцию языка, которая поможет сократить количество строк кода. Не редко уровень программиста на Python можно определить с помощью этой конструкции.

Пример кода:

For x in xrange(5, 10): if x % 2 == 0: x =* 2 else: x += 1

Цикл с условием, подобные встречаются не редко. А теперь попробуем эти 5 строк превратить в одну:

>>>

Недурно, 5 строк или 1. Причем выразительность повысилась и такой код проще понимать - один комментарий можно на всякий случай добавить.

В общем виде эта конструкция такова:

Стоит понимать, что если код совсем не читаем, то лучше отказаться от такой конструкции.

Анонимные функции или lambda

Продолжаем сокращать количества кода.

Def calc(x, y): return x**2 + y**2

Функция короткая, а как минимум 2 строки потратили. Можно ли сократить такие маленькие функции? А может не оформлять в виде функций? Ведь, не всегда хочется плодить лишние функции в модуле. А если функция занимает одну строчку, то и подавно. Поэтому в языках программирования встречаются анонимные функции, которые не имеют названия.

Анонимные функции в Python реализуются с помощью лямбда-исчисления и выглядят как лямбда-выражения:

>>> lambda x, y: x**2 + y**2 at 0x7fb6e34ce5f0>

Для программиста это такие же функции и с ними можно также работать.

Чтобы обращаться к анонимным функциям несколько раз, присваиваем переменной и пользуемся на здоровье.

>>> (lambda x, y: x**2 + y**2)(1, 4) 17 >>> >>> func = lambda x, y: x**2 + y**2 >>> func(1, 4) 17

Лямбда-функции могут выступать в качестве аргумента. Даже для других лямбд:

Multiplier = lambda n: lambda k: n*k

Использование lambda

Функции без названия научились создавать, а где использовать сейчас узнаем. Стандартная библиотека предоставляет несколько функций, которые могут принимать в качестве аргумента функцию - map(), filter(), reduce(), apply().

map()

Функция map() обрабатывает одну или несколько последовательностей с помощью заданной функции.

>>> list1 = >>> list2 = [-1, 1, -5, 4, 6] >>> list(map(lambda x, y: x*y, list1, list2)) [-7, 2, -15, 40, 72]

Мы уже познакомились с генератором списков, давайте и воспользуемся если длина список одинаковая):

>>> [-7, 2, -15, 40, 72]

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

filter()

Функция filter() позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:

>>> numbers = >>> list(filter(lambda x: x < 5, numbers)) # В результат попадают только те элементы x, для которых x < 5 истинно

То же самое с помощью списковых выражений:

>>> numbers = >>>

reduce()

Для организации цепочечных вычислений в списке можно использовать функцию reduce(). Например, произведение элементов списка может быть вычислено так (Python 2):

>>> numbers = >>> reduce(lambda res, x: res*x, numbers, 1) 720

Вычисления происходят в следующем порядке:

((((1*2)*3)*4)*5)*6

Цепочка вызовов связывается с помощью промежуточного результата (res). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):

>>> reduce(lambda res, x: res*x, , 1) 1

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

>>> reduce(lambda res, x: [x]+res, , )

Для наиболее распространенных операций в Python есть встроенные функции:

>>> numbers = >>> sum(numbers) 15 >>> list(reversed(numbers))

В Python 3 встроенной функции reduce() нет, но её можно найти в модуле functools.

apply()

Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно (Python 2):

>>> def f(x, y, z, a=None, b=None): ... print x, y, z, a, b ... >>> apply(f, , {"a": 4, "b": 5}) 1 2 3 4 5

В Python 3 вместо функции apply() следует использовать специальный синтаксис:

>>> def f(x, y, z, a=None, b=None): ... print(x, y, z, a, b) ... >>> f(*, **{"a": 4, "b": 5}) 1 2 3 4 5

На этой встроенной функции закончим обзор стандартной библиотеки и перейдем к последнему на сегодня функциональному подходу.

Замыкания

Функции, определяемые внутри других функций, представляют собой замыкания. Зачем это нужно? Рассмотрим пример, который объяснит:

Код (вымышленный):

Def processing(element, type_filter, all_data_size): filters = Filter(all_data_size, type_filter).get_all() for filt in filters: element = filt.filter(element) def main(): data = DataStorage().get_all_data() for x in data: processing(x, "all", len(data))

Что можно в коде заметить: в этом коде переменные, которые живут по сути постоянно (т.е. одинаковые), но при этом мы загружаем или инициализируем по несколько раз. В итоге приходит понимание, что инициализация переменной занимает львиную долю времени в этом процессе, бывает что даже загрузка переменных в scope уменьшает производительность. Чтобы уменьшить накладные расходы использовать замыкания.

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

Научимся оформлять замыкания:

Def multiplier(n): "multiplier(n) возвращает функцию, умножающую на n" def mul(k): return n*k return mul # того же эффекта можно добиться выражением # multiplier = lambda n: lambda k: n*k mul2 = multiplier(2) # mul2 - функция, умножающая на 2, например, mul2(5) == 10

Заключение

В уроке мы рассмотрели базовые понятия ФП, а также составили список механизмов, которые будут рассмотрены в следующих уроках. Поговорили о способах уменьшения количества кода, таких как cписковые включения (генератор списка), lamda функции и их использовании и на последок было несколько слов про замыкания и для чего они нужны.

Язык Python не зря пользуется популярностью в среде программистов Гугла и редакторов Хакера одновременно:). Этот поистине мощный язык позволяет писать код, следуя нескольким парадигмам, и сегодня мы попробуем разобраться, в чем же между ними разница и какой из них лучше следовать.

Какие парадигмы?! Давайте кодить!

Когда тебе надо написать что-то, то ты, наверное, меньше всего заморачиваешься относительно того, какую парадигму программирования выбрать. Скорее, ты либо выбираешь наиболее подходящий язык, либо сразу начинаешь кодить на своем любимом, предпочитаемом и годами проверенном. Оно и верно, пусть об идеологии думают идеологи, наше дело – программить:). И все-таки, программируя, ты обязательно следуешь какой-либо парадигме. Рассмотрим пример. Попробуем написать что-нибудь простое… ну, например, посчитаем площадь круга.

Можно написать так:

Площадь круга (вариант первый)

double area_of_circle(double r) {
return M_PI*pow(r,2);
}
int main() {
double r = 5;
cout << "Площадь: "<< area_of_circle(r)<< endl;
}

А можно и так:

Площадь круга (вариант второй)

class Circle {
double r;
public:
Circle(double r) { this->r = r; }
double area() { return M_PI*pow(this->r,2); }
void print_area() {
cout << "Площадь: "<< this->area() << endl;
}
};
int main() {(new Circle(5))->print_area();}

Можно и по-другому… но только как не старайся, код будет или императивным (как в первом случае), или объектно-ориентированным (как во втором).
Это происходит не из-за отсутствия воображения, а просто потому, что C++ «заточен» под эти парадигмы.

И лучшее (или худшее, в зависимости от прямоты рук), что с его помощью можно сделать – это смешать несколько парадигм.

Парадигмы

Как ты уже догадался, на одном и том же языке можно писать, следуя нескольким парадигмам, причем иногда даже нескольким сразу. Рассмотрим основные их представители, ведь без этих знаний ты никогда не сможешь считать себя профессиональным кодером, да и о работе в команде тебе тоже, скорее всего, придется забыть.

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

«Сначала делаем это, потом это, затем вот это»

Языки: Почти все

Абсолютно понятная любому программисту парадигма: «Человек дает набор инструкций машине».
С императивной парадигмы все начинают учить/понимать программирование.

Функциональное программирование

«Считаем выражение и используем результат для чего-нибудь еще».

Языки: Haskell, Erlang, F#

Абсолютно непонятная начинающему программисту парадигма. Мы описываем не последовательность состояний (как в императивной парадигме), а последовательность действий.

Объектно-ориентированное программирование

«Обмениваемся сообщениями между объектами, моделируя взаимодействия в реальном мире».

Языки: Почти все

Объектно-ориентированная парадигма со своим появлением прочно вошла в нашу жизнь.
На ООП построены практически все современные бизнес-процессы.

Логическое программирование

«Отвечаем на вопрос поиском решения».

Языки: Prolog

Логическое программирование – довольно специфическая штука, но, в то же время, интересная и интуитивно понятная.
Достаточно простого примера:

{задаем правила}
witch(X) <= burns(X) and female(X).
burns(X) <= wooden(X).
wooden(X) <= floats(X).
floats(X) <= sameweight(duck, X).
{задаем наблюдения}
female(girl).
sameweight(duck,girl).
{задаем вопрос}
? witch(girl).

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

Функциональное программирование противопоставляют императивному.

Императивное программирование подразумевает последовательность изменений состояния программы, а переменные служат для хранения этого состояния.

Функциональное программирование, наоборот, предусматривает последовательность действий над данными. Это сродни математике – мы долго пишем на доске формулу f(x), а потом подставляем x и получаем результат.

И вся соль функционального программирования в том, что здесь формула – это инструмент, который мы применяем к иксу.

Двуликий питон

Нет лучшей теории, чем практика, так что давай уже что-нибудь напишем. А еще лучше – напишем на питоне:).
Посчитаем сумму квадратов элементов массива «data» императивно и функционально:

Императивный Питон

data = [...]
sum = 0
for element in a:
sum += element ** 2
print sum

Функциональный Питон

data = [...]
sq = lambda x: x**2
sum = lambda x,y: x+y
print reduce(sum, map(sq, data))

Оба примера на питоне, хотя я и не включил его в список функциональных языков. Это не случайность, поскольку полностью функциональный язык – довольно специфичная и редко используемая штука. Первым функциональным языком был Lisp, но даже он не был полностью функциональным (ставит в тупик, не правда ли?). Полностью функциональные языки используются для всякого рода научных приложений и пока не получили большого распространения.

Но если сами «функционалы» и не получили широкого распространения, то отдельные идеи перекочевали из них в скриптинговые (и не только) языки программирования.
Оказалось, что совершенно необязательно писать полностью функциональный код, достаточно украсить императивный код элементами функционального.

Питон в действии

Оказывается, концепции ФП реализованы в Питоне более чем изящно. Ознакомимся с ними подробнее.

?-исчисления

Lambda исчисления – это математическая концепция, которая подразумевает, что функции могут принимать в качестве аргументов и возвращать другие функции.
Такие функции называются функциями высших порядков. ?-исчисления основываются на двух операциях: аппликация и абстракция.
Я уже привел пример аппликации в предыдущем листинге. Функции map, reduce – это и есть те самые функции высших порядков, которые «апплицируют», или применяют, переданную в качестве аргумента функцию к каждому элементу списка (для map) или каждой последовательной паре элементов списка (для reduce).

Что касается абстракции – здесь наоборот, функции создают новые функции на основе своих аргументов.

Lambda-абстракция

def add(n):
return lambda x: x + n

adds =

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

Замыкание – это определение функции, зависящей от внутреннего состояния другой функции. В нашем примере это lambda x. С помощью этого приема мы делаем что-то похожее на использование глобальных переменных, только на локальном уровне.

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

Таким образом, мы можем написать код, который работает не только с переменными, но и функциями, что дает нам еще несколько «степеней свободы».

Чистые функции и ленивый компилятор

Императивные функции могут изменять внешние (глобальные) переменные, и это значит, что функция может возвращать различные значения при одних и тех же значениях аргумента на разных стадиях выполнения программы.

Такое утверждение совсем не подходит для функциональной парадигмы. Здесь функции рассматриваются как математические, зависящие только от аргументов и других функций, за что они и получили прозвище «чистые функции».

Как мы уже выяснили, в функциональной парадигме можно распоряжаться функциями как угодно. Но больше всего выгоды мы получаем, когда пишем «чистые функции». Чистая функция – это функция без побочных эффектов, а значит, она не зависит от своего окружения и не изменяет его состояния.

Применение чистых функций дает нам ряд преимуществ:

  • Во-первых, если функции не зависят от переменных окружения, то мы уменьшаем количество ошибок, связанных с нежелательными значениями этих самых переменных. Вместе с количеством ошибок мы уменьшаем и время отладки программы, да и дебагить такие функции гораздо проще.
  • Во-вторых, если функции независимы, то компилятору есть, где разгуляться. Если функция зависит только от аргументов, то ее можно посчитать только один раз. В следующие разы можно использовать кэшированное значение. Также, если функции не зависят друг от друга, их можно менять местами и даже автоматически распараллеливать.

Для увеличения производительности в ФП также используются ленивые вычисления. Яркий пример:

print length()

По идее, на выходе мы должны получить ошибку деления на ноль. Но ленивый компилятор питона просто не станет вычислять значения каждого элемента списка, так как его об этом не просили. Нужна длина списка – пожалуйста!
Те же принципы используются и для других языковых конструкций.

В результате несколько «степеней свободы» получает не только программист, но и компилятор.

Списочные выражения и условные операторы

Чтобы жизнь (и программирование) не казались тебе медом, разработчики питона придумали специальный «подслащающий» синтаксис, который буржуи так и называют – «syntactic sugar».
Он позволяет избавиться от условных операторов и циклов… ну, если не избавиться, то уж точно свести к минимуму.

В принципе, ты его уже видел в предыдущем примере – это adds = . Здесь мы сразу создаем и инициализируем список значениями функций. Удобно, правда?
Еще есть такая штука, как операторы and и or, которые позволяют обходиться без громоздких конструкций типа if-elif-else.

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

Императивный код

L =
for x in xrange(10):
if x % 2 == 0:
if x**2>=50:
L.append(x)
else:
L.append(-x)
print L

Функциональный код

print

Итоги

Как ты уже понял, необязательно полностью следовать функциональной парадигме, достаточно умело использовать ее в сочетании с императивной, чтобы упростить себе жизнь. Однако, я все время говорил про императивную парадигму… и ничего не сказал про ООП и ФП.

Что ж, ООП – это, фактически, надстройка над императивной парадигмой, и если ты перешел от ИП к ООП, то следующим шагом должно быть применение ФП в ООП. В заключение скажу пару слов об уровне абстракции. Так вот, чем он выше – тем лучше и именно сочетание ООП и ФП дает нам этот уровень.

CD

На диск я положил свежие дистрибутивы питона для виндусоидов. Линуксоидам помощь не нужна:).

WWW

Несколько хороших ресурсов для тех, кому хочется узнать больше:

INFO

Если тебе не приглянулся питон, то не расстраивайся – ты можешь успешно применять идеи функционального программирования и в других языках высокого уровня.

3.2.3 Dictionary Comprehensions

Say we have a dictionary the keys of which are characters and the values of which map to the number of times that character appears in some text. The dictionary currently distinguishes between upper and lower case characters.

We require a dictionary in which the occurrences of upper and lower case characters are combined:

dct = { "a" : 10 , "b" : 34 , "A" : 7 , "Z" : 3 }

frequency = { k . lower () : dct . get (k . lower () , 0 ) + dct . get (k . upper () , 0 )

for k in dct . keys () }

print frequency # {"a": 17, "z": 3, "b": 34}

Python supports the creation of anonymous functions (i.e. functions that are not bound to a name) at runtime, using a construct called “lambda”. This is not exactly the same as lambda in functional programming languages, but it is a very powerful concept that’s well integrated into Python and is often used in conjunction with typical functional concepts like filter() , map() and reduce() .

Anonymous functions in the form of an expression can be created using the lambda
statement:

args is a comma-separated list of arguments, and expression is an expression involving those arguments. This piece of code shows the difference between a normal function definition and a lambda function:

def function (x ) :

return x * x

print function (2 ) # 4

#-----------------------#

function = lambda x : x * x

print function (2 ) # 4

As you can see, both function() do exactly the same and can be used in the same ways. Note that the lambda definition does not include a “return” statement - it always contains an expression which is returned. Also note that you can put a lambda definition anywhere a function is expected, and you don’t have to assign it to a variable at all.

The following code fragments demonstrate the use of lambda functions.

def increment (n ) :

return lambda x : x + n

print increment (2 ) # at 0x022B9530>

print increment (2 ) (20 ) # 22

The above code defines a function increment that creates an anonymous function on the fly and returns it. The returned function increments its argument by the value that was specified when it was created.

You can now create multiple different increment functions and assign them to variables, then use them independent from each other. As the last statement demonstrates, you don’t even have to assign the function anywhere - you can just use it instantly and forget it when it’s not needed anymore.

Q3. What is lambda good for?
Ans.
The answer is:

  • We don’t need lambda, we could get along all right without it. But…
  • there are certain situations where it is convenient - it makes writing code a bit easier, and the written code a bit cleaner.

Q4. What kind of situations?

Well, situations in which we need a simple one-off function: a function that is going to be used only once.

Normally, functions are created for one of two purposes: (a) to reduce code duplication, or (b) to modularize code.

  • If your application contains duplicate chunks of code in various places, then you can put one copy of that code into a function, give the function a name, and then - using that function name - call it from various places in your code.
  • If you have a chunk of code that performs one well-defined operation - but is really long and gnarly and interrupts the otherwise readable flow of your program - then you can pull that long gnarly code out and put it into a function all by itself.

But suppose you need to create a function that is going to be used only once - called from only one place in your application. Well, first of all, you don’t need to give the function a name. It can be “anonymous”. And you can just define it right in the place where you want to use it. That’s where lambda is useful.

Typically, lambda is used in the context of some other operation, such as sorting or a data reduction:

names = [ "David Beazley" , "Brian Jones" , "Raymond Hettinger" , "Ned Batchelder" ]

print sorted (names , key = lambda name : name . split () [ - 1 ] . lower () )

# ["Ned Batchelder", "David Beazley", "Raymond Hettinger", "Brian Jones"]

Although lambda allows you to define a simple function, its use is highly restricted. In
particular, only a single expression can be specified, the result of which is the return
value. This means that no other language features, including multiple statements, conditionals, iteration, and exception handling, can be included.
You can quite happily write a lot of Python code without ever using lambda. However,
you’ll occasionally encounter it in programs where someone is writing a lot of tiny
functions that evaluate various expressions, or in programs that require users to supply
callback functions.

You’ve defined an anonymous function using lambda, but you also need to capture the
values of certain variables at the time of definition.

>>> x = 10

>>> a = lambda y : x + y

>>> x = 20

>>> b = lambda y : x + y

Now ask yourself a question. What are the values of a(10) and b(10)? If you think the
results might be 20 and 30, you would be wrong:

The problem here is that the value of x used in the lambda expression is a free variable
that gets bound at runtime, not definition time. Thus, the value of x in the lambda
expressions is whatever the value of the x variable happens to be at the time of execution.
For example:

If you want an anonymous function to capture a value at the point of definition and
keep it, include the value as a default value, like this:

The problem addressed here is something that tends to come up in code that
tries to be just a little bit too clever with the use of lambda functions. For example,
creating a list of lambda expressions using a list comprehension or in a loop of some kind and expecting the lambda functions to remember the iteration variable at the time of definition. For example:

>>> funcs = [ lambda x : x + n for n in range (5 ) ]