了解 toolz 用例

understanding toolz use cases

我正在学习一些函数式编程并正在研究 toolz。 compose、pipe、thread_first 和 thread_last 之间的差异对我来说似乎非常微妙或根本不存在。这些功能的预期不同用例是什么?

  • compose 对比 thread_*pipe

    compose本质上是一个function compostion(∘)。它的主要目标是将不同的功能组合成 可重用 块。应用程序的顺序与参数的顺序相反,因此 compose(f, g, h)(x)f(g(h(x)))(与 (f ∘ g)(x) 相同 f (g(x))).

    thread_*pipe 是关于使用可重用块来创建单个数据流。只能通过惰性操作延迟执行,但块是固定的。应用顺序与参数顺序相同,因此 pipe(x, f, g, h)h(g(f(x))).

  • compose 对比 thread_*.

    compose 不允许附加参数,而 thread_* 允许。没有柯里化 compose 只能与一元函数一起使用。

    相对于thread_可以使用更高阶的函数,包括常用的高阶函数:

    thread_last(
        range(10),
        (map, lambda x: x + 1),
        (filter, lambda x: x % 2 == 0)
    )
    

    compose 相同,您需要柯里化:

    pipe(
        range(10), 
        lambda xs: map(lambda x: x + 1, xs), 
        lambda xs: filter(lambda x: x % 2 == 0, xs)
    )
    

    from toolz import curried
    
    pipe(
        range(10), 
        curried.map(lambda x: x + 1), 
        curried.filter(lambda x: x % 2 == 0)
    )
    
  • thread_first 对比 thread_last.

    thread_first 将管道参数放在函数的第一个位置。

    thread_last 将管道参数放在函数的最后位置。

    例如

    >>> from operator import pow
    >>> thread_last(3, (pow, 2))   # pow(2, 3)
    8
    >>> thread_first(3, (pow, 2))  # pow(3, 2)
    9
    

在实践中(忽略一些形式主义)这些函数通常是可以互换的,尤其是当与 functools.partial / toolz.curry 和一些 lambda 表达式结合使用时,但根据上下文,它是一个比另一个更方便。

例如,对于内置的高阶函数,如 mapfunctools.reducethread_last 是一个自然的选择。如果你想在多个地方重用一段代码,使用 compose(h, g, f) 比添加函数包装器 def fgh(x) pipe(x, f, g, h) 更好。等等。