Что такое чистая функция python

Функциональное программирование на Python. Часть 1. Общие вопросы

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

Парадигмы программирования

Для начала обратимся к википедии за определением понятия “парадигма программирования”. Парадигма программирования — это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером.

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

Декларативное программирование предполагает ответ на вопрос “Что?”. Здесь вы описываете задачу, даете спецификацию, говорите, что вы хотите получить в результате выполнения программы, но не определяете, как этот ответ будет получен.

Каждая из этих парадигм включает в себя более специфические модели. В промышленности наибольшее распространение получили структурное и объектно-ориентированное программирование из группы “императивное программирование” и функциональное программирование из группы “декларативное программирование”.

Довольно часто бывает так, что дизайн языка позволяет использовать все перечисленные парадигмы (например Python ) или только часть из них (например, C++ ).

В чем суть функционального программирования?

Языки, которые можно отнести в функциональной парадигме обладают определенным набором свойств. Если язык не является чисто функциональным, но реализует эти свойства, то на нем можно разрабатывать, как говорят, в функциональном стиле. Свойства функционального стиля программирования:

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

Примеры работы в функциональном стиле

Функции, как объекты первого класса

Рекурсия

Рекурсия – это вызов функции из нее же самой. Для начала приведем пример не рекурсивной функции, которая считает факториал:

В качестве результата получим число 120.

Перепишем ее через рекурсию:

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

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

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

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

Например, следующая функция модифицирует данные, которые в нее передаются:

P.S.

Источник

Мир Python: функционалим по-маленьку — Python для продвинутых

Введение

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

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

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

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

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

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

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

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

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

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

Неизменяемые данные

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

filter()

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

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

reduce()

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

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

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

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

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

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

apply()

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

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

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

Замыкания

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

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

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

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

Заключение

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

Что такое чистая функция python. Смотреть фото Что такое чистая функция python. Смотреть картинку Что такое чистая функция python. Картинка про Что такое чистая функция python. Фото Что такое чистая функция python

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

Об обучении на Хекслете

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

Источник

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

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

Парадигма функционального программирования

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

Функциональное программирование, несмотря на кажущуюся сложность, несёт в себе ряд преимуществ:

— Код становится короче;

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

Примерами функциональных языков являются LISP (Clojure), Haskell, Scala, R.

Функциональное программирование, как и логическое программирование, нашло большое применение в теории искусственного интеллекта и её приложениях.

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

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

Это концепция является основной в функциональном программировании. Чистые функции удовлетворяют двум условиям:

1. Функция, вызываемая от одних и тех же аргументов, всегда возвращает одинаковое значение. Например, если происходит вызов функции sum(2, 3), то ожидается, что результат всегда будет равен 5. При вызове же функции rand(), или при обращении к переменной, не определённой в функции, чистота функции нарушается, а это в функциональном программировании недопустимо.

Функции высших порядков.

Рекурсия.

В функциональных языках цикл обычно реализуется в виде рекурсии, так как в функциональной парадигме программирования отсутствует такое понятия, как цикл. Рекурсивные функции вызывают сами себя, позволяя операции выполняться снова и снова. Для использования рекурсии может потребоваться большой стек, но этого можно избежать в случае хвостовой рекурсии. Хвостовая рекурсия может быть распознана и оптимизирована компилятором в код, получаемый после компиляции аналогичной итерации в императивном языке программирования. Оптимизировать хвостовую рекурсию можно путём преобразования программы в стиле использования продолжений при её компиляции, как один из способов.

Переменные неизменяемы.

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

Лямбда-исчислении

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

Краткая история функционального программирования

Теория так и оставалась теорией, пока в конце 1950-х годов Джон Маккарти не разработал язык Лисп, который стал первым почти функциональным языком программирования и многие годы оставался единственным таковым. Лисп всё ещё используется (также как и Фортран), после многих лет эволюции он удовлетворяет современным запросам, которые заставляют разработчиков программ взваливать как можно большую ношу на компилятор, облегчив так свой труд. Нужда в этом возникла из-за всё более возрастающей сложности программного обеспечения.

В связи с этим обстоятельством всё большую роль начинает играть типизация. В конце 70-х — начале 80-х годов XX века интенсивно разрабатываются модели типизации, подходящие для функциональных языков. Большинство этих моделей включали в себя поддержку таких мощных механизмов как абстракция данных и полиморфизм. Появляется множество типизированных функциональных языков: ML, Scheme, Hope, Miranda, Clean и многие другие. Вдобавок постоянно увеличивается число диалектов.

В семидесятых в университете Эдинбурга Робин Милнер создал язык ML, а Дэвид Тернер начинал разработку языка SASL в университете Сент-Эндрюса и, впоследствии, язык Miranda в университете города Кент. В конечном итоге на основе ML были созданы несколько языков, среди которых наиболее известные Objective Caml и Standard ML. Также в семидесятых осуществлялась разработка языка программирования, построенного по принципу Scheme (реализация не только функциональной парадигмы), получившего описание в известной работе «Lambda Papers», а также в книге восемьдесят пятого года «Structure and Interpretation of Computer Programs».

В результате вышло так, что практически каждая группа, занимающаяся функциональным программированием, использовала собственный язык. Это препятствовало дальнейшему распространению этих языков и порождало многие более мелкие проблемы. Чтобы исправить положение, объединённая группа ведущих исследователей в области функционального программирования решила воссоздать достоинства различных языков в новом универсальном функциональном языке. Первая реализация этого языка, названного Haskell в честь Хаскелла Карри, была создана в начале 90-х годов.

Большинство функциональных языков программирования реализуются как интерпретируемые, следуя традициям Лиспа (примечание: большая часть современных реализаций Лиспа содержат компиляторы в машинный код). Таковые удобны для быстрой отладки программ, исключая длительную фазу компиляции, укорачивая обычный цикл разработки. С другой стороны, интерпретаторы в сравнении с компиляторами обычно проигрывают по скорости выполнения. Поэтому помимо интерпретаторов существуют и компиляторы, генерирующие машинный код (например, Objective Caml) или код на С/С++ (например, Glasgow Haskell Compiler). Практически каждый компилятор с функционального языка реализован на этом же самом языке. Это же характерно и для современных реализаций Лиспа, кроме того среда разработки Лиспа позволяет выполнять компиляцию отдельных частей программы без остановки программы (вплоть до добавления методов и изменения определений классов).

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

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

Достоинства функционального программирования:

Недостатки функционального программирования::

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

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

Поддержка функционального программирования в Python

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

Функции в Python

Функции в Python определяются 2-мя способами: через определение def или через анонимное описание lambda. Оба этих способа определения доступны, в той или иной степени, и в некоторых других языках программирования. Особенностью Python является то, что функция является таким же именованным объектом, как и любой другой объект некоторого типа данных, например, как целочисленная переменная. В листинге представлен пример объявления одной и той же функции разными способами:

При вызове всех трёх объектов-функций мы получим один и тот же результат:

Введите число: 13
&lt class ‘function’ &gt : &lt function pow3 at 0x03425270 &gt
arg = 13.0 => fun(arg) = 2197.0
&lt class ‘function’ &gt : &lt function pow3 at 0x03425270 &gt
arg = 13.0 => fun(arg) = 2197.0
&lt class ‘function’ &gt : &lt function pow3 at 0x03425270 &gt
arg = 13.0 => fun(arg) = 2197.0

В Python версии 3, в которой всё является классами (в том числе, и целочисленная переменная), функции являются объектами программы, принадлежащими к классу function.

Если функциональные объекты Python являются такими же объектами, как и другие объекты данных, значит, с ними можно и делать всё то, что можно делать с любыми данными:

— динамически изменять в ходе выполнения;

— встраивать в более сложные структуры данных (коллекции);

— передавать в качестве параметров и возвращаемых значений и т.д.

На этом (манипуляции с функциональными объектами как с объектами данных) и базируется функциональное программирование. Python, конечно, не является настоящим языком функционального программирования, так, для полностью функционального программирования существуют специальные языки: Lisp, Planner, а из более свежих: Scala, Haskell. Ocaml. Но в Python можно «встраивать» приёмы функционального программирования в общий поток императивного (командного) кода, например, использовать методы, заимствованные из полноценных функциональных языков. Т.е. «сворачивать» отдельные фрагменты императивного кода (иногда достаточно большого объёма) в функциональные выражения.

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

Достаточно часто при программировании на Python используют типичные конструкции из области функционального программирования, например:

В результате запуска получаем:

Элементы функционального программировании в Python

Основными элементами функционального программирования в Python являются следующие функции: lambda, map, filter, reduce, zip.

lambda выражение

lambda оператор или lambda функция в Python это способ создать анонимную функцию, то есть функцию без имени. Такие функции можно назвать одноразовыми, они используются только при создании. Как правило, lambda функции используются в комбинации с функциями filter, map, reduce.

Синтаксис lambda выражения в Python:

В качестве arguments передается список аргументов, разделенных запятой, после чего над переданными аргументами выполняется expression. Если присвоить lambda-функцию переменной, то получим поведение как в обычной функции:

Но все преимущества lambda-выражений состоят в использовании lambda в связке с другими функциями.

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

На выводе получим отсортированный словарь в виде списка:

Кроме того, можно создавать списки lambda-выражений, которые позволяют получить список действий, выполняемых по требованию:

Также можно создавать таблицы действий с помощью словарей, значениями которых являются lambda-выражения:

Функция map()

Тот же эффект мы можем получить, применив функцию map:

В результате запуска получим тоже самое:

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

А теперь то же самое, только используя lambda выражение:

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

Если же количество элементов в списках совпадать не будет, то выполнение закончится на минимальном списке:

Функция filter()

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

Функция, передаваемая в filter должна возвращать значение True или False, чтобы элементы корректно отфильтровались.

Функция reduce()

Функция reduce принимает 2 аргумента: функцию и последовательность. reduce() последовательно применяет функцию-аргумент к элементам списка, возвращает единичное значение.

Вычисление суммы всех элементов списка при помощи reduce:

Вычисление наибольшего элемента в списке при помощи reduce:

Функция zip()

Функция zip объединяет в кортежи элементы из последовательностей переданных в качестве аргументов.

Функция zip прекращает выполнение, как только достигнут конец самого короткого списка.

Замыкание в Python

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

Вот как срабатывает такая динамически определённая функция:

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

Частичное применение функции

Сравнение замыкания, частичного определения и функтора:

Вызов всех трёх конструкций для аргумента, равного 5, приведёт к получению одинакового результата, хотя при этом и будут использоваться абсолютно разные механизмы:

Карринг

Это преобразование было введено М. Шейнфинкелем и Г. Фреге и получило своё название в честь математика Хаскелла Карри, в честь которого также назван и язык программирования Haskell.

Карринг не относится к уникальным особенностям функционального программирования, так карринговое преобразование может быть записано, например, и на языках Perl или C++. Оператор каррирования даже встроен в некоторые языки программирования (ML, Haskell), что позволяет многоместные функции приводить к каррированному представлению. Но все языки, поддерживающие замыкания, позволяют записывать каррированные функции, и Python не является исключением в этом плане.

В листинге 8 представлен пример с использованием карринга:

Вот как выглядит результат этих вызовов:

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

Литература

6. Харрисон Д. Введение в функциональное программирование

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *