В этой статье вы узнаете, что такое функциональная парадигма и как использовать Python для функционального программирования. Вы также узнаете о списковом понимании и других формах понимания.
функциональная парадигма
В императивной парадигме задачи выполняются путем предоставления компьютеру последовательности инструкций и последующего их выполнения. Когда эти инструкции выполняются, некоторые состояния могут быть изменены. Например, предположим, что вы сначала установили A равным 5, а затем изменили значение A. В этот момент вы изменяете состояние A в смысле значения внутри переменной.
В функциональной парадигме вы не говорите компьютеру, что делать, вы говорите ему, в чем дело. Например, каков наибольший общий делитель числа, каково произведение от 1 до n и так далее.
Следовательно, переменная не может измениться. Как только вы установите переменную, она останется такой навсегда (обратите внимание, в чисто функциональных языках они не являются переменными). Поэтому функциональное программирование не имеет побочных эффектов. Побочные эффекты — это когда функция изменяет что-то кроме себя. Давайте посмотрим на несколько примеров типичного кода Python:
Вывод этого кода равен 5. В функциональной парадигме изменение переменных является большим запретом, и наличие функции, которая влияет на вещи, выходящие за рамки ее области действия, также является большим запретом. Единственное, что может сделать функция, это что-то вычислить и вернуть в результате.
Теперь вы можете подумать: "Нет переменных, нет побочных эффектов? Почему это хорошо?" Это хороший вопрос, и я уверен, что большинство людей задаются им.
Если функция вызывается дважды с одними и теми же аргументами, она гарантированно возвращает один и тот же результат. Если вы выучили математические функции, вы будете знать это преимущество. Это называется ссылочной прозрачностью. Поскольку функции не имеют побочных эффектов, если вы создаете программу, которая что-то вычисляет, вы можете ускорить программу. Если каждый вызов func(2) возвращает 3, мы можем сохранить его в таблице, что предотвратит повторный запуск одной и той же функции программой.
Обычно в функциональном программировании мы не используем циклы. Мы используем рекурсию. Рекурсия — это математическое понятие, которое обычно означает «вызов самого себя». Используйте рекурсивную функцию, которая многократно вызывает себя как подфункцию. Вот хороший пример рекурсивной функции в Python:
Некоторые языки программирования также ленивы. Это означает, что они ничего не рассчитывают и не делают до последней секунды. Если вы напишете какой-нибудь код, чтобы сделать 2 + 2, программа функции будет вычисляться только тогда, когда вам действительно нужно использовать результат. Скоро мы рассмотрим лень в Python.
Map
Чтобы понять, давайте сначала посмотрим, что такое итерация. Обычно объекты, которые можно перебирать, представляют собой списки или массивы, но в Python есть много разных типов для перебора. Вы даже можете создавать свои собственные объекты, которые можно повторять, реализуя магические методы. Магические методы похожи на API, который помогает вашим объектам стать более питоническими. Вам нужно реализовать 2 магических метода, чтобы сделать объект итерируемым:
Первый магический метод "__iter__" (примечание: здесь двойное подчеркивание) возвращает итерируемый объект, который обычно используется в начале цикла. "__next__" возвращает следующий объект.
Давайте быстро зайдем в терминал и вызовем приведенный выше код:
будет распечатан
В Python итератор — это объект, имеющий только магический метод __iter__. Это означает, что вы можете получить доступ к местоположению в объекте, но не можете перемещаться по объекту. Некоторые объекты будут иметь магический метод __next__ вместо магического метода __iter__, например коллекции (обсуждаемые далее в этой статье). В этой статье мы предположим, что все, к чему мы прикасаемся, является повторяемыми объектами.
Теперь, когда мы знаем, что такое итерируемый объект, давайте вернемся к функции карты. Функция карты позволяет нам применять функцию к каждому элементу в итерируемом объекте. Карта принимает 2 входа: применяемую функцию и итерацию.
Предположим, у нас есть такой список чисел:
Мы хотим возвести каждое число в квадрат, мы можем написать такой код:
Функциональные функции в Python ленивы. Если мы не используем «список», функция будет хранить определение итерируемого объекта, а не сам список. Нам нужно явно указать Python «превратить его в список» для нашего использования.
Внезапный переход от неленивого к ленивому вычислению в Python немного странен. Если вы больше думаете функциональным способом мышления, чем императивным, вы в конце концов привыкнете к нему.
Теперь написать обычную функцию типа «квадрат(число)» можно, но неправильно. Мы должны определить целую функцию, чтобы использовать ее на карте? Что ж, мы можем определить функцию на карте, используя лямбда-функцию (анонимную).
Лямбда-выражения
Лямбда-выражение — это функция, состоящая всего из одной строки. Например, это лямбда-выражение возводит заданное число в квадрат:
Запустим:
Разве это не похоже на функцию?
Хм, это немного запутанно, но объяснимо. Мы присваиваем что-то переменной «квадрат». Что насчет этого:
Скажите Python, что это лямбда-функция с входом, называемым x. Все, что после двоеточия, это то, что вы сделали с вводом, и оно автоматически возвращает результат.
Упростив нашу квадратную программу до одной строки кода, мы можем сделать это:
Итак, в лямбда-выражении все параметры находятся слева, а то, что вы собираетесь с ними делать, — справа. Это немного грязно. Но правда в том, что в написании кода есть определенное удовольствие, которое могут прочитать только другие функциональные программисты. Кроме того, довольно круто взять функцию и превратить ее в одну строку кода.
Reduce
Reduce — это функция, которая превращает итерацию в вещь. Часто вы можете выполнять вычисления со списком, используя функцию сокращения, чтобы уменьшить его до числа. Сокращение выглядит так:
Мы часто используем лямбда-выражения в качестве функций.
Произведение списков — это каждое отдельное число, умноженное вместе. Для этого вы должны написать такой код:
Но с сокращением вы можете написать:
Получите ту же функциональность, более короткий и аккуратный код, если вы используете функциональное программирование. (Примечание: функция сокращения больше не является встроенной функцией в Python3, и ее необходимо импортировать из модуля functools)
Filter
Функция filter принимает итерацию и отфильтровывает все, что вам не нужно в этой итерации.
Обычно для фильтра требуется функция и список. Он применяет функцию к каждому элементу в списке, и если функция возвращает значение True, она ничего не делает. Если он возвращает False, элемент удаляется из списка.
Синтаксис следующий:
Давайте посмотрим на небольшой пример, без фильтра мы бы написали:
Используя фильтр, вы можете написать:
Python, как постоянно развивающийся и популярный язык, все еще обновляется. При обучении рекомендуется найти нескольких партнеров по учебе для совместного изучения и обсуждения, эффект будет лучше. Если вы хотите изучать Python, присоединяйтесь к группе обмена обучением Python (627012464), контролируйте и учитесь вместе. Есть инструменты разработки, много галантереи и технической информации, которыми можно поделиться!
Функции высшего порядка
Функции высшего порядка могут принимать функции в качестве аргументов и возвращать функции. Вот очень простой пример:
Пример второй возвращающей функции:
Я сказал в начале, что в чистых языках функционального программирования нет переменных. Функции высшего порядка облегчают эту задачу.
Все функции в Python являются гражданами первого класса. Граждане первого сорта определяются как обладающие одной или несколькими из следующих характеристик:
Создано во время выполнения
Размещение переменных или элементов в структуре данных
Передано как параметр в функцию
возвращается как результат функции
Все функции в Python можно использовать как функции более высокого порядка.
Partial application
Частичные приложения (также известные как замыкания) — это немного странно, но очень круто. Вы можете вызвать функцию без указания всех необходимых параметров. Давайте посмотрим это на примере. Мы хотим создать функцию, которая принимает 2 аргумента, основание и показатель степени, и возвращает основание в степени показателя степени, например:
Теперь нам нужна специальная функция возведения в квадрат, которая возводит число в квадрат с помощью функции степени:
Это работает, но что, если нам нужна функция куба? Или как насчет функции четвертой силы? Можем ли мы продолжить и записать их? Ну, ты можешь. Но программисты ленивы. Если вы повторяете одно и то же снова и снова, это признак того, что есть более быстрый способ ускорить процесс, который удержит вас от повторения. Здесь мы можем использовать замыкания. Давайте посмотрим на пример квадратной функции с использованием замыкания:
Разве это не круто! Мы можем вызвать функцию, которая требует 2 аргумента, только с 1 аргументом.
Мы также можем использовать цикл для генерации функции степени, которая реализует кубы вплоть до степени 1000.
Функциональное программирование не является pythonic
Вы могли заметить, что многое из того, что мы хотим сделать в функциональном программировании, вращается вокруг списков. Все функции, которые вы видите, кроме функций сокращения и замыканий, генерируют списки. Гвидо (отцу Python) не нравится функциональный стиль Python, потому что Python уже имеет собственный способ генерации списков.
Если вы напишете «импортировать это» в интерактивной среде Python, вы получите:
Это дзен Python. Это стихотворение о том, что значит быть Pythonic. Части, которые мы хотим охватить:
Должен быть один — и желательно только один — очевидный способ сделать это.
В Python map и filter могут выполнять те же операции, что и генераторы списков (обсуждается ниже). Это нарушает правило дзен Python, поэтому эти части функционального программирования не считаются «питоновскими».
Еще одна тема — лямбда. В Python лямбда-функция — это обычная функция. Лямбды - это синтаксический сахар. Эти два утверждения эквивалентны.
Обычная функция может делать все то же, что и лямбда-функция, но не наоборот. Лямбда-функция не может делать все, что может обычная функция.
Вот краткий аргумент, почему функциональное программирование не очень хорошо сочетается со всей экосистемой Python. Возможно, вы заметили, что я упоминал списковые включения ранее, мы обсудим их сейчас.
понимание списка
Ранее я упомянул, что все, что вы можете сделать с картой или фильтром, вы можете сделать и со списком. Понимание списков — это способ создания списков в Python. Синтаксис:
Возведем в квадрат каждое число в списке, например:
Мы можем видеть, как функция применяется к каждому элементу в списке. Как мы применяем фильтры? Взгляните на предыдущий код:
Мы можем превратить это в понимание списка следующим образом:
Перечисляет операторы поддержки, такие как if. Вам больше не нужно применять миллион функций к чему-то, чтобы получить то, что вы хотите. На самом деле, если вы пытаетесь создать какой-то список, будет проще и понятнее использовать понимание списка. Что, если бы мы захотели возвести в квадрат каждое число меньше 0 в списке? С лямбдой, картой и фильтром вы бы написали:
Это кажется длинным и сложным. С пониманием списка это просто:
Понимание списков работает только со списками. map, filter подходит для любого итерируемого объекта, так что толку от этого? Вы можете использовать любой вывод с любой итерацией, с которой вы столкнетесь.
другие производные
Вы можете создать вывод для любого итерируемого объекта.
Любая итерация может быть сгенерирована с использованием понимания. Начиная с Python 2.7, вы даже можете создавать словари (хэш-карты).
Если это итерируемый объект, его можно сгенерировать. Давайте посмотрим на последний набор примеров.
Набор — это список элементов, в котором ни один элемент не повторяется дважды.
Элементы в множестве не имеют порядка.
Вы можете заметить, что set (коллекция) имеет те же фигурные скобки, что и dict (словарь). Питон очень умен. В зависимости от того, предоставляете ли вы значения для dict, он будет знать, пишете ли вы понимание dict или понимание набора.
Суммировать
Функциональное программирование прекрасно и чисто. Функциональный код может быть чистым, но он также может быть грязным. Некоторым программистам Python не нравится функциональное программирование в Python. Но на мой взгляд, вы должны использовать лучшие инструменты для решения проблем.