Как стать автором
Обновить

Комментарии 14

Пайпы выглядят полезно.


Частичные фунцкии вроде же можно делать так:


def f_partitial (x,y,z):
    return x+y+z

my_partial_f = lambda z: f_partial(1, 2, z)

my_partial_f(3) # same as: f_partial(1, 2, 3)

Или я что-то не так понял?

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


def f (x,y,z):
    return x+y+z
my_pf = f(1,2)
my_pf(3)

Во первых это снизит читаемость из за неявного изменения поведению только по отсуствию параметра. Что если я забыл его передать? Или параметры со значением по умолчанию? Или *args?


Например:


def f (x, y, z=0):
    return x+y+z

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


Явное лучше неявного.

Да, пожалуй вы правы

А нельзя вместо пайплайна просто написать функцию, которой передать как аргументы имена функций — элементов пайплайна?
Я против введения новых элементов в язык. Зачем еще один ад, уже есть C++. Если скучно, можно им заняться.
Выскажу мнение всех программистов-не-функциональщиков:
что пайплайны, что «однострочные» решения совершенно нечитаемы.
(Суб)оптимальным по читабельности будет что-то в духе:

def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function
    def powered_sum(x):
        digits = [int(symbol) for symbol in str(x)]
        return sum([v**(i+1) for i, v in enumerate(digits)])
    return [i for i in range(a, b + 1) if powered_sum(i) == i]

Далее — некий псевдокод, вдохновленный соседним data science — языком:


range(a, b+1) |
    keep(x -> 
         x |         
         str |
         map(int) |
         map2((v, i) -> v **(i+1)) |
         sum |
         y -> eq(y, x))

Я уверен, что можно отформатировать и сделать еще более читаемым.
Собственно это мало чем отличается от предложенного автором, смысл же заключается в том, что выражение (а это именно выражение) читается слева-направо, сверху-вниз — и именно в этом порядке происходит обработка данных. В вашем примере приходится несколько раз переключатся между разными строками чтобы понять что происходит.

Обработка данных компьютером — да, а вот человеком — под вопросом.
Неподготовленный человек мыслит более декларативно. И читабельный код — он читабельный для человека. А как человек описал бы алгоритм?
1. Возьмем цифры числа. Цифры — это целочесленные значения (int) символов (symbol) цифр в строке (in str(x)).
2. Далее возьмем сумму (sum) степеней цифр (v ^ (i + 1)) от позиции в строке (enumerate(digits)).
3. Постановка задачи: найти те числа (i) в интервале от a до b (range(1, b+1)), для которых сумма степеней цифр равна самому числу (powered_sum(i) == i).
Как видите, предложеная процедурная запись полностью совпадает со структурой русского языка. Поэтому это читабельно.
Врочем, моя позиция в том, что я верю, что Вам вполне так же удобно в парадигме функционального программирования, как мне в литературного программирования.

Почему это f(1)(2)(3) не должно работать?
По синтаксису питона, это всё разлагается на f.__call__(1).__call__(2).__call__(3), всё левоассоциативное, лишние скобки не нужны.


И кстати, не нужны классы-шмаклассы. Вложенные функции отлично справляются.


def curry(f):
    arity = len(getfullargspec(f)[0])
    # для простоты, забьём на функции с переменным числом аргументов!!!

    def make(*bound):
        def partial(*args):
            combo = bound + args
            assert len(combo) <= arity
            if len(combo) == arity:
                return f(*combo)
            else:
                return make(*combo)
        # для простоты, забьём на создание документации
        return partial

    return make()

Однострочники в стиле питона, на list (generator) comprehensions, более-менее отлично читаются, просто незачем их в одну строку пихать.


def solve_that(a, b):
    return (
        n                       # сразу ответ, что делаем: возвращаем n
        for n in range(a, b+1)  # в заданном интервале
        if n == sum(            # отвечающие условию
            di ** i
            for i, ci in enumerate(str(n))
            for di in [int(ci)]  # лайфхак, присваивание внутри for-выражения
            # ну, или можно было выше написать int(ci) ** i
        )
    )

Ну и раз зашла пьянка про конвееры, то чуть компактнее (и без deepcopy)


class PipeValue:
    def __init__(self, data):
        self.data = data
    def __or__(self, next_step):
        return PipeValue(next_step(self.data))

class PipeFun:
    def __init__(self, steps = []):
        self.steps = steps
    def __or__(self, next_step):
        return PipeFun(self.steps + [next_step])
    def __call__(self, data):
        return reduce(lambda d, step: step(d), self.steps, data)
    def __rrshift__(self, data):
        return PipeValue(self(data))

def maps(f):
    return lambda series: map(f, series)

(PipeValue('hello') | list | maps(ord) | sum).data
(PipeFun() | list | maps(ord) | sum)('hello')
('hello' >> PipeFun() | list | maps(ord) | sum).data
('hello' >> (PipeFun() | list | maps(ord) | sum)).data
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории