Статьи

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

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

 

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

Существует три парадигмы программирования:  
императивное программирование ,
функциональное программирование и
логическое программирование . Большинство языков программирования поддерживают только императивный стиль. Императивный стиль имеет прямое отношение к машинному языку. Функции императивных стилей, такие как оператор присваивания, условное выполнение и циклы, напрямую получены из машинных языков. Процедурные и объектно-ориентированные языки программирования, такие как C, C ++, Java, являются обязательными языками программирования. Логическое программирование — это совершенно другой стиль, оно не будет содержать решения проблемы, а написано с точки зрения фактов и правил. Prolog, ASP & DataLog — некоторые из языков логического программирования.


 

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

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

Неизменность . По умолчанию функциональные языки должны поддерживать неизменяемые объекты, изменяемый объект должен быть объявлен явно и осознанно. Напротив, языки императивных стилей по умолчанию поддерживают изменяемые объекты, также неизменные объекты должны быть объявлены явно из другой библиотеки. 
Функция — это граждане первого класса. Функции — это граждане первого класса, и она обрабатывается как любой другой объект. Это означает, что функции могут быть сохранены как объект, функции могут быть переданы в качестве аргумента другим функциям, функция может вернуть функцию. 
Лексическая область видимости : в функциональном программировании область действия функции зависит от места ее определения. Если функция определена внутри функции, то ее область видимости находится только внутри внешней функции. Он не может быть передан вне внешней функции.

 

3. Тип функций

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

Функция может принимать другую функцию в качестве аргумента и может возвращать функцию.

def FindFormula(shape):
    if (shape == "Square"):
        return SquareArea
    if (shape == "Circle"):
        return CircleArea

def SquareArea(a):
    return (a * a)

def CircleArea(r):
    return (22/7*r * r)

def findShape(sides):
    if (sides == 4):
        return "Square"
    if (sides == 0 ):
        return "Circle"

if __name__ == "__main__":

    size = 5
    area = FindFormula(findShape(sides=4))(size)
    print("Area = ",area)

3.2 Анонимные функции


Функция без имени называется анонимной функцией.
Обычно эти функции определены внутри других функций и вызываются немедленно.
area = lambda x : 22/7*x*x
print(area(10))

Анонимные функции создаются с помощью ключевого слова «лямбда». Мы можем использовать этот синтаксис, где нам нужна функция.

 
3.3. Вложенные функции

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

def oFunction(x,y):
    def SqArea(x,y):
    return (x*y)
return SqArea(x,y)
sum = oFunction(10,20)

В приведенном выше примере функция SqArea строго находится в области действия oFunction ().

3.4 Карри


Простое определение Кей С. Хартсманна в его книге «Scala для нетерпеливых»:
каррирование — это процесс превращения функции, которая принимает два аргумента, в функцию, которая принимает один аргумент. Эта функция возвращает функцию, которая использует второй аргумент. Обобщенное определение для карри: функция, которая принимает несколько аргументов и превращается в цепочку функций, каждая из которых принимает один аргумент и возвращает следующую функцию, пока последняя не вернет результат.
def curried_pow(x):
     def h(y):
         return pow(x,y)
     return h
print curried_pow(2)(3)

3.5 Закрытия


Закрытие — это постоянная область действия, которая сохраняет локальные переменные даже после того, как выполнение кода вышло из этого блока.
Внутренняя функция, которая запоминает состояние внешней функции, даже после того, как внешняя функция завершила выполнение. Обычно такое поведение невозможно в стилях императивного программирования, поскольку функция выполняется в отдельном кадре стека. Как только функция завершает свое выполнение, кадр стека освобождается. Но в функциональном программировании, пока внутренняя функция запоминает состояние внешней функции, кадр стека не освобождается.
def sumA(a):
    def sumB(b):
        return(a+b)
    return(sumB)
x = sumA(10)
y = x(20)
print (y)

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

3.6 Хвостовая рекурсия

Рекурсивная функция вызывается как Tail Recursive, когда последний оператор в функции выполняет только рекурсивные вызовы. Это намного эффективнее, чем рекурсивная функция, потому что, поскольку рекурсивный вызов является последним оператором, в текущем вызове функции нечего сохранять в стеке. Следовательно, хвостовая рекурсия очень легка в использовании памяти. Регулярная рекурсивная функция для факториала в Python:

def fact(n):
    if (n == 0):
        return 1
    else:
        return n * fact(n-1)

То же самое можно преобразовать в рекурсию хвоста, как показано ниже, с дополнительным аргументом:

def fact(n):
         tailFact(n,1)

def tailFact(n,a):
    if (n == 0):
        return(a)
    else:
        return tailFact(n-1,n*a)

4 Преимущества функционального программирования

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

Параллелизм : программирование для многоядерной архитектуры — это сложная задача, когда один фрагмент кода может выполняться несколькими потоками параллельно. Таким образом, возникнут проблемы с повторным вводом кода и областями общей памяти. В программировании с императивным стилем мы должны использовать методы синхронизации, чтобы избежать этих проблем, которые приведут к снижению производительности. Философия стиля функционального программирования соответствует потребностям многоядерного программирования, обеспечивая неизменность. 
Эффективность памяти : функциональный стиль программирования эффективен для памяти. Анонимные функции, вложенные функции и стили хвостовой рекурсии очень легки в памяти. Из-за лексической области видимости, как только функция выйдет из области видимости, она будет удалена из памяти, и хвостовая рекурсия очень эффективна, если не удерживать кадры стека.

5 Резюме

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