Функциональные корни Python
Python попадет под влияние функциональных языков, независимо от того что люди говорят или думают.
🕛 14.01.2011, 13:49
Операции над списками использовались к каждому элементу списка и создавали новый перечень. К примеру:def square(x):return x*xvals = [1, 2, 3, 4]newvals = []for v in vals:newvals.append(square(v))На функциональных языках, таких как Lisp и Scheme, операции, подобные как эта были разработаны как встроенные задачи языка. Т.о. пользователи, знакомые с такими языками, реализовывали аналогичную функциональность в Python. К примеру:
def map(f, s):result = []for x in s:result.append(f(x))return resultdef square(x):return x*xvals = [1, 2, 3, 4]newvals = map(square,vals)Тонкость вышеупомянутого кода в том, что многим людям не нравился факт, что роль используется к элементам списка как набор отдельных задач. Языки, подобные как Lisp позволили функциям определяться "налету" при маппинге. К примеру, в Scheme Вы можете делать анонимные задачи и выполнить маппинг в единственном выражении, используя лямбду, как ниже:
(map (lambda (x) (* x x)) '(1 2 3 4))
Впрочем в Python задачи были объектами "I-го класса", у него не было никакого такого механизма для того, чтоб делать анонимные задачи.
В итоге 1993, пользователи метались кругом разных идей, чтоб обеспечить анонимные задачи. К примеру, Марк Луц написал код задачи, которая создает задачи, используя exec:
def genfunc(args, expr):exec('def f(' + args + '): return ' + expr)return eval('f')# Sample usagevals = [1, 2, 3, 4]newvals = map(genfunc('x', 'x*x'), vals)
Тим Петерс написал решениерешения, которое упростило синтаксис, разрешая пользователям следующее:
vals = [1, 2, 3, 4]newvals = map(func('x: x*x'), vals)
Стало понятно, что на самом деле возникло потребность в подобной функциональности. Одновременно, это чудилось заманчиво - установить анонимные задачи как строки кода, которые программист обязан руками обработать ч/з exec. Т.о., в январе 1994, map(), filter(), и reduce() задачи были добавлены к стандартной библиотеке. Более того, был представлен оператор лямбды для того, чтоб делать анонимные задачи (как выражения) в более прямом синтаксисе. К примеру:
vals = [1, 2, 3, 4]newvals = map(lambda x:x*x, vals)
Эти дополнения были значительными на ранней стадии разработки. К несчастью, я не помню автора, и логи SVN не записали его. Если это Ваша заслуга, оставьте комментарий!
Мне никогда не нравилось применять терминологию "лямбды", однако из-за отсутствия лучшей и очевидной альтернативы, она была принята в Python. В конечном счете это был выбор сейчас анонимного автора, и в то время крупные перемены требовали гораздо меньшего числа обсуждений чем сейчас.
Лямбда была предназначена на самом деле лишь, чтоб быть синтаксическим сахаром для определения анонимных задач. Хотя, у подобного выбора терминологии было немало непреднамеренных последствий. К примеру, пользователи, знакомые с функциональными языками, ожидали, что семантика лямбды будет соответствовать онной из иных языков. В итоге они обнаружили, что претворении в жизнь Python крайне недоставало расширенного функционала. К примеру, сложность с лямбдой заключается в том, что предоставленное выражение не могло обратиться к переменным в окружающем контексте. К примеру, если бы у Вас данный код, map() прервется, так как роль лямбды работала бы с неопределенной ссылкой на переменную 'a'.
def spam(s):a = 4r = map(lambda x: a*x, s)
Существовали обходные ответы данной трудности, однако они заключались в использовании "аргументов изначально" и передачу скрытых параметров в лямбда-выражение. К примеру:
def spam(s):a = 4r = map(lambda x, a=a: a*x, s)
"Корректное" решение данной трудности для внутренних задач было в том, чтоб неявно перенести ссылки на все локальные переменные в контекст задачи. Этот подход известен как "замыкание" и является важным аспектом функциональных языков. Хотя, эта возможность не была представлена в Python до выпуска версии 2.2.
Интересно, что map, filter, и reduce задачи, которые изначально смотивировали введение лямбды, и иных функциональных возможностей были в огромной степени заменены на "list comprehensions" и генераторы. Буквально, роль reduce была удалена из списка встроенных задач в Python 3.0.
Даже при том, что я не задумывал Python как функциональный язык, введение замыканий было полезно в создании прочих возможностей языка. К примеру, некоторые аспекты классов нового стиля, декораторов, и иных современных возможностей применяют замыкания.
Наконец, даже при том, что немало возможностей функционального программирования были введены за эти годы, Python все еще испытывает недостаток в определенном функционале из "настоящих" функциональных языков. К примеру, Python не выполняет некоторые виды оптимизации (к примеру, хвостовая рекурсия). Динамический характер Python сделал невозможным ввести оптимизации времени компиляции, известные в функциональных языкых как Haskell или ML.