【问题标题】:What is the Python equivalent of these Haskell higher-order functions?这些 Haskell 高阶函数的 Python 等价物是什么?
【发布时间】:2013-02-10 17:30:50
【问题描述】:

Learn You a Haskell For Great Good 中关于偏函数的章节包含以下代码:

multThree :: (Num a) => a -> a -> a -> a
multThree x y z = x * y * z

ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
ghci> let multWithEighteen = multTwoWithNine 2
ghci> multWithEighteen 10
180

我目前正在使用 Python 中的 functools 库,并设法使用它复制这些函数的行为。

from functools import partial

def multThree(x,y,z):
  return x * y * z

>>> multTwoWithNine = partial(multThree,9)
>>> multTwoWithNine(2,3)
>>> multWithEighteen = partial(multTwoWithNine,2)
>>> multWithEighteen(10)
180

我现在想做的一件事是看看我是否可以从同一本书的章节中复制一些更有趣的高阶函数,例如:

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys

但是,我不确定如何执行此操作,或者 partial() 在这里是否有用。

【问题讨论】:

  • 您应该编辑和修复您的函数,确保它们在语法上都是正确的。例如,您的第一个缺少其论点。
  • 从技术上讲,您必须在任何转换中使用 partial,因为所有 Haskell 函数都是自动柯里化的,而 partial 模拟了柯里化函数必须部分应用的能力。或者您可以将 python 版本编写为 curried 函数,但是您必须像 foo(a)(b)(c) 一样调用它们。
  • 次要注意:你想要“部分应用函数”而不是“部分函数”。

标签: python haskell


【解决方案1】:

Python 的内置 map 函数的行为类似于 Haskell 的 zipWith:

>>> def add(x,y): return x + y
... 
>>> map(add,[1,2,3],[10,20,30])
[11, 22, 33]

【讨论】:

  • 地图是内置的而不是在库中定义的?
  • 我想我希望能解释一下为什么(必须?)内置。
  • 历史原因:map 长期以来一直是内置函数,至少可以追溯到 90 年代中期的 1.x 版本。 functools 和 itertools 是最近添加的,反映了通过标准库中的单独模块和包添加功能的更新理念。
【解决方案2】:
def add(a, b):
    return a + b

x = [1, 2, 3, 4]
y = [5, 6, 7, 8]

>> map(add, x, y)
[6, 8, 10, 12]

另外,请查看 Python 内置 itertools 模块:http://docs.python.org/2/library/itertools.html

【讨论】:

    【解决方案3】:

    此 Python 代码的行为类似于您提供的 zipWith' 函数:

    def zip_with(f, l1, l2):
        if len(l1) == 0 or len(l2) == 0:
            return []
        else:
            return [f(l1[0], l2[0])] + zip_with(f, l1[1:], l2[1:])
    

    不过,与 Haskell 函数相比,此函数有几个缺点。首先是它看起来不太好,因为 Python 没有模式匹配语法;我们必须改用len、[0] 和[1:]。第二个是 Python 函数不以任何方式使用惰性求值,因此 zip_with 将始终遍历整个列表,即使它可以提前停止。第三个是这个函数对结果列表的每个元素调用一次,Python 的递归限制大约(或者确切地说?)1,000,所以如果输出列表的长度超过大约 1,000 个元素,这个函数将引发异常.

    第二个和第三个问题可以使用生成器来解决。

    【讨论】:

    • 你也可以使用[f(l1.pop(),l2.pop())] + zip_with(f, l1, l2),所以不需要丑陋的[0]或[1:]
    【解决方案4】:

    这是使用内置 zip 函数和列表理解的好选择:

    >>> zip_with = lambda fn, la, lb: [fn(a, b) for (a, b) in zip(la, lb)]
    
    >>> add2 = lambda x,y: x+y
    >>> zip_with(add2, range(10), range(1,11))
    [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
    

    【讨论】:

      猜你喜欢
      • 2018-11-30
      • 1970-01-01
      • 2017-08-04
      • 2019-05-24
      • 2011-01-20
      • 2014-04-26
      • 2021-08-14
      • 2023-04-05
      • 2011-05-20
      相关资源
      最近更新 更多