Функциональный конвейер с использованием python с опережающими операторами - PullRequest
0 голосов
/ 06 мая 2018

Я слежу за докладом PyData в https://youtu.be/R1em4C0oXo8, докладчик, которому нужна библиотека для конвейерного вызова yamal. Эта библиотека не с открытым исходным кодом. Таким образом, пытаясь воспроизвести FP на python, я попытался воспроизвести основы этой библиотеки.

В двух словах, вы строите серию pure функций в python (f1, f2, f3 и т. Д.) И создаете их список следующим образом:

pipeline = [f1, f2, f3, f4]

Затем вы можете применить функцию run_pipeline, и результатом будет композиция:

f4(f3(f2(f1)))

Требования к функциям состоят в том, что все они имеют одно возвращаемое значение, и, кроме f1, все имеют один вход.

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

def run_pipeline(pipeline):
    get_data, *rest_of_steps = steps
    def compose(x):
       for f in rest_of_steps:
          y = f(x)
          x = y
       return x

    data = get_data()
    return compose(data)

В ток-шоу показано более продвинутое использование этой абстракции, он определяет «операторы» fork и reducer. Эти «операторы» позволяют запускать конвейеры следующим образом:

pipeline1 = [ f1, fork(f2, f3), f4 ]

, что эквивалентно: [ f4(f2(f1)), f4(f3(f1)) ]

и

pipeline2 = [ f1, fork(f2, f3), f4, reducer(f5) ]

, что эквивалентно f5([f4(f3(f1)), f4(f2(f1))]).

Я пытаюсь решить эту проблему с помощью функционального программирования , но я просто не могу. Я не знаю, являются ли fork и reducer decorators (и если да, то как мне передать список следующих функций?), Не знаю, следует ли мне преобразовать этот список в граф, используя объекты? сопрограммы? (может быть, все это чепуха) Я просто совершенно запутался.

Может ли кто-нибудь помочь мне с тем, как создать это с помощью python и функционального программирования ?

ПРИМЕЧАНИЕ : В видео он рассказывает о наблюдателях или исполнителях. для этого упражнения мне плевать на них.

1 Ответ

0 голосов
/ 07 мая 2018

Хотя эта библиотека предназначена для поддержки FP в Python, неясно, должна ли сама библиотека быть написана с использованием большого количества FP.

Это один из способов реализовать использование классов (основанных на типе list), чтобы сообщить функции pipe, нужно ли ее разбирать или уменьшать, и имеет ли она дело с одним элементом данных или списком элементов. .

Это ограничивает использование техник стиля FP, таких как рекурсивные вызовы apply_func (допускается использование нескольких форков в конвейере).

class Forked(list):
    """ Contains a list of data after forking """

class Fork(list):
    """ Contains a list of functions for forking """

class Reducer(object):
    """ Contains a function for reducing forked data """
    def __init__(self, func):
        self.func = func

def fork(*funcs):
    return Fork(funcs)

def reducer(func):
    """ Return a reducer form based on a function that accepts a
        Forked list as its first argument """
    return Reducer(func)   

def apply_func(data, func):
    """ Apply a function to data which may be forked """
    if isinstance(data, Forked):
        return Forked(apply_func(datum, func) for datum in data)
    else:
        return func(data)

def apply_form(data, form):
    """ Apply a pipeline form (which may be a function, fork, or reducer) 
        to the data """
    if callable(form):
        return apply_func(data, form)
    elif isinstance(form, Fork):
        return Forked(apply_func(data, func) for func in form)
    elif isinstance(form, Reducer):
        return form.func(data)

def pipe(data, *forms):
    """ Apply a pipeline of function forms to data """
    return reduce(apply_form, forms, data)

Примеры использования:

def double(x): return x * 2
def inc(x): return x + 1
def dec(x): return x - 1
def mult(L): return L[0] * L[1]

print pipe(10, inc, double)    # 21
print pipe(10, fork(dec, inc), double)  # [18, 22]
print pipe(10, fork(dec, inc), double, reducer(mult))   # 396

РЕДАКТИРОВАТЬ: Это также можно немного упростить, сделав fork функцию, которая возвращает функцию, и reducer класс, который создает объекты, имитирующие функцию. Тогда отдельные классы Fork и Reducer больше не нужны.

class Forked(list):
    """ Contains a list of data after forking """

def fork(*funcs):
    """ Return a function that will take data and output a forked
    list of results of putting the data through several functions """
    def inner(data):
        return Forked(apply_form(data, func) for func in funcs)

    return inner

class reducer(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, data):
        return self.func(data)

def apply_form(data, form):
    """ Apply a function or reducer to data which may be forked """
    if isinstance(data, Forked) and not isinstance(form, reducer):
        return Forked(apply_form(datum, form) for datum in data)
    else:
        return form(data)

def pipe(data, *forms):
    """ Apply a pipeline of function forms to data """
    return reduce(apply_form, forms, data)
...