Передача двух функций в третью двоичную функцию в Python - PullRequest
0 голосов
/ 04 декабря 2018

Я медленно пытаюсь войти в функциональное программирование на Python и столкнулся со следующей проблемой:

Учитывая две функции f1 и f2, как я могу построить функцию f, которая умножает эти двафункции с одним и тем же аргументом «функционально»?

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

f = lambda x : f1(x) * f2(x)

, но это как-то не помоглоКажется, я не в правильном духе функционального программирования.

Моя следующая попытка заключалась в использовании таких операторов mul и juxt, как этот

>>> from tools import juxt
>>> from operator import mul
>>> f = mul(juxt(f1,f2))
TypeError: op_mul expected 2 arguments, got 1

Попытка разбитьвывод кортежей juxt с * тоже не работал:

>>> f = mul(*juxt(f1, f2))
TypeError: mul() argument after * must be an iterable, not juxt

Повторное использование lambda, похоже, сработало, но это каким-то образом победило всю цель ...

>>> temp  = juxt(f_1, f_2)
>>> f = lambda x : mul(*temp(x))

Может быть, я здесь слишком педантичен или неблагодарен (для Python), но мне кажется, что я упускаю что-то очень важное или рутинное в функциональном программировании.

Есть ли более функциональный способ сделать это

Ответы [ 2 ]

0 голосов
/ 04 января 2019

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

class AddableFunction:
    '''
    Function decorator that lets (f+g)(x) = f(x) + g(x).
    '''
    def __init__(self, function):
        self._function = function
    def __call__(self, *args, **kwargs):
        return self._function(*args, **kwargs)
    def __add__(self, other):
        return AddableFunction(lambda *args, **kwargs: self(*args, **kwargs) + other(*args, **kwargs))

@AddableFunction
def f(x):
    return x ** 2

@AddableFunction
def g(x):
    return x ** 3

print((f + g)(1)) # 2
print((f + g)(2)) # 12
print((f + g)(3)) # 36
0 голосов
/ 05 декабря 2018

TL; DR Такая композиция является примитивной (в том смысле, что она не может быть разложена на другие функции более высокого порядка) операцией, которую не поддерживают ни Python, ни модуль tools.Вам нужно реализовать это самостоятельно.


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

  1. compose позволяет связать вместе две функции.То есть

    compose(f,g) == lamba x: f(g(x))
    
  2. curry относится к частичному применению: демонстрация будет быстрее, чем объяснение:

    curry(f)(x)(y) == f(x, y)
    

    То есть curry(f)(x) в основном совпадает с partial(f, x);оба принимают значение y, чтобы вернуть значение f(x, y).

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

map(f, [a,b,c]) == [f(a), f(b), f(c)]

Функции являются и функторами, но вместо map мы используем compose.То есть отображение f поверх g дает compose(f, g).

Теперь, чтобы объединить mul, f1 и f2 в g = lambda x: g(f1(x), f2(x)), кажется, что оба composeи curry было бы полезно.

lambda x: mul(f1(x), f2(x)) == lambda x: curry(mul)(f1(x))(f2(x))

и

lambda x: mul(f1(x), f2(x)) == lambda x: compose(curry(mul), f1)(x)(f2(x))

(т. Е. curry - это то, что позволяет нам составить функцию с двумя аргументами с другой функцией.)

Но композиция в некотором смысле строго линейная операция;ввод одной функции происходит от вывода другой.Композиция mul и f1 создает функцию, которая ожидает аргумент , а возвращает функцию, которая ожидает тот же аргумент.Как мы можем переместить x из «середины» любого выражения?Нам нужна какая-то загадочная функция foo такая, что

foo(f, g) = lambda x: f(x, g(x))

, которая делает функцию, которая передает свой аргумент обоим f и g, одновременно передавая результатg(x) до f.С такой функцией foo мы могли бы написать

lambda x: foo(compose(curry(mul), f1), f2)

и получить желаемый результат.

И это подводит нас к идее аппликативного функтора.Он обеспечивает необходимую функцию foo

def foo(f, g):
   def _(x):
       return f(x, g(x))

, которая объединяет понятия композиции и карри, которых у нас нет на данный момент.

Другими словами, foo - это отличная примитивная операция;Вы не можете реализовать это с точки зрения самой композиции.

...