【问题标题】:Clojure style function "threading" in PythonPython中的Clojure风格函数“线程”
【发布时间】:2011-06-25 01:33:15
【问题描述】:

Clojure 有一个“->”宏,它递归地插入每个表达式作为下一个表达式的第一个参数。

这意味着我可以写:

(-> arg f1 f2 f3)

它的行为类似于(外壳管道):

f3(f2(f1(arg)))

我想在 Python 中执行此操作;然而,搜索似乎是一场噩梦!搜索不到“->”,也搜索不到Python函数threading

有没有办法让 | 重载?运算符,以便我可以用 Python 编写它?

arg | f1 | f2 | f3

谢谢!

【问题讨论】:

标签: python functional-programming clojure


【解决方案1】:

我相信一个非常“Pythonic”的方法是像这样线程函数:

thread_functions(my_arg)(func1, func2, ...)

或者,如果您有多个初始参数:

thread_functions(arg1, arg2, ...)(func1, func2, ...)

要实现上述内容,可以这样做:

def thread_functions(*init_args):
    def execute_functions(*functions_list):
        x = functions_list[0](*init_args)

        for func in functions_list[1:]:
            try:
                x = func(*x)
            except TypeError:
                x = func(x)

        return x

return execute_functions

函数thread_functions 接收初始参数集(可以有很多)并返回函数execute_functions。调用时,execute_functions 接收一组要迭代的函数,将上一次迭代的结果作为参数应用到当前函数。它还处理可迭代和不可迭代的参数。

this GitHub Gist我留下了一些其他的解释并实现了一些例子来更好地展示用例。

【讨论】:

    【解决方案2】:

    或者试试https://mdk.fr/blog/pipe-infix-syntax-for-python.html 提供如下语法的模块:

      fib() | take_while(lambda x: x < 1000000)
            | where(lambda x: x % 2)
            | select(lambda x: x * x)
            | sum()
    

    【讨论】:

      【解决方案3】:

      聚会有点晚了,但这里有一个更干净的方法,imo。将满足大多数 FP 需求。

      def stream(*args):
          return reduce(lambda a, t: t[0](t[1], a), args[1:], args[0])
      

      一个基本的map、filter、reduce:

      >>> my_list = [1, 2, 3, 4, 5]
      >>> stream(my_list, 
      ...    (map,    lambda x: x ** 2),
      ...    (filter, lambda x: x < 20),
      ...    (reduce, lambda a, x: a + x))
      30
      

      【讨论】:

        【解决方案4】:

        pytoolz 库中有一个thread function(实际上有两个;它们对多个参数的函数做的事情略有不同)。

        还有一个名为cytoolz 的pytoolz 库的cython 实现,它可能更有效。可以使用pip进行安装。

        【讨论】:

          【解决方案5】:

          以霍华德的解决方案为基础:

          def T(*args):
            return reduce(lambda l, r: r(l), args)
          
          def dbl(n):
              return 2*n
          
          T(5,dbl,dbl)
          #=> 20
          
          T(5,dbl,dbl,lambda x: 3*x)
          #=> 60
          

          【讨论】:

            【解决方案6】:

            虽然我对创建很酷的新语言结构(如 Lisp 宏)表示同情,但这样做并不是真正的 Python 哲学:

            >>> import this
            [...]
            There should be one-- and preferably only one --obvious way to do it.
            

            但正如受访者所说,您可以通过多种方式进行函数链接。如果您喜欢的话,这里可能更明确地类似于 Lisp:

            a = lambda x: x*2
            b = lambda x: x+1
            
            def chain(first, *args):
                if len(args) == 0:
                    return first
                else:
                    return first(chain(*args))
            
            print chain(b, a, 1)
            

            【讨论】:

            • Python 的 lisp 影响使它变得如此出色。
            • Lisp 在 Python 中的适用性取决于所讨论的项目。潜在开发者社区的人口统计数据在这里非常重要。编写解析库?为胜利而生!做数字?使用 Lisp 并失去 90% 的开发人员池。
            【解决方案7】:

            或者可能通过以下方式使用reduce函数:

            reduce(lambda x,f : f(x), [f1,f2,f3], arg)
            

            【讨论】:

            • 这是一种非常简洁实用的实现方式。谢谢!呈现的其他方式也一样,但“arg”在前面,我认为这对编写代码的流程有很大的影响。
            • @Vimal:如果您担心排序,可以使用 reduce(lambda x,f : f(x), [arg,f1,f2,f3])。
            【解决方案8】:

            您可以自己轻松实现类似的东西。

            def compose(current_value, *args):
                for func in args:
                    current_value = func(current_value)
                return current_value
            
            def double(n):
                return 2*n
            
            print compose(5, double, double) # prints 20
            

            【讨论】:

              【解决方案9】:

              不,没有(至少是理智的)。你也不想。为什么不直接写f3(f2(f1(arg)))?或者更好的是,以不需要递归的方式为您的问题建模。

              您可以通过将表达式包装在一个类中并在该类中定义 __or__ 来重载 |,但是,看在 Guido 的份上,请不要这样做。

              你也可以做btilly 写的,但我也不建议这样做。在语言为您提供的范围内工作。

              【讨论】:

              • 呵呵,为什么突然提到递归?
              • @Jochen -&gt; 在 Clojure 中本质上是递归的
              • 我知道 Guido 不喜欢函数式技术,但这没有理由不告诉喜欢它们的人如何在 Python 中使用它们。
              • 可读性取决于品味和具体情况。例如,从左到右、从上到下的 Schwartzian 变换更容易理解。至于性能,如果它是您的主要关注点,那么您根本不应该使用 Python。
              • @JochenRitzel,在 Clojure 中,-&gt; 不是“递归的”。
              猜你喜欢
              • 1970-01-01
              • 2011-08-22
              • 1970-01-01
              • 2012-08-08
              • 1970-01-01
              • 2020-07-02
              • 2016-01-11
              • 2022-06-16
              • 2022-09-16
              相关资源
              最近更新 更多