Python: Haskell-喜欢。 / $
Python: Haskell-like . / $
在Haskell中我会写:
main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
in Python 我将不得不使用许多括号或无用的变量...在 Python 中是否有类似 .
和 $
的东西?
(我不熟悉 Haskell,但如果我正确理解您的代码片段...)
您可以使用列表理解来执行过滤和取幂。
[i**2 for i in range(1,21) if i%2 == 0]
因为 map 函数 returns 可迭代的列表和过滤器也可以嵌套它们 -
map(function1, (filter(function2,list)))
有关更多信息,我建议您阅读 map function documentation also filter function documentation
对于这种情况,您最好使用@CoryKramer 所说的列表理解。
要在 Python 中应用部分应用程序,您应该使用 functools.partial
,就像这样
from functools import partial
def compose(func1, *func2):
return func1 if not func2 else lambda x: func1(compose(*func2)(x))
myMap = partial(map, lambda x: x**2)
myFilter = partial(filter, lambda x: x%2 == 0)
myFunction = compose(myMap, myFilter)
myFunction(range(20))
我会 使用任何可用的惯用 Python 工具,例如其他人指出的列表理解,而不是试图假装你在写Haskell,但如果你真的必须,你甚至可以在 Python:
中使用 compose
组合函数
# this is essentially just foldr (or right `reduce`) specialised on `compose2`
def compose(*args):
ret = identity
for f in reversed(args):
ret = compose2(f, ret)
return ret
def identity(x): return x
def compose2(f, g): return lambda x: f(g(x))
你可以这样使用:
from functools import partial
# equiv. of: map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
这确实有效:
>>> compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
... 但是 如您所见,Python 缺少某些概念,例如柯里化和任意可定义的中缀运算符,因此即使在语义上,上面的片段代码与 Haskell 片段等效(甚至相同),它读起来非常糟糕。
至于 $
运算符: 它在 Python 中几乎没有关系 — 它在 Haskell 中的主要目的与运算符优先级有关,这在 Python 中不是问题,因为大多数时候你不能真正使用运算符,而且所有内置运算符都有预定义的优先级。
而 $
还可以用作 Haskell 中的高阶函数:
zipWith ($) [(3*), (4+), (5-)] [1,2,3]
...在 Python 中复制它及其(已弃用)apply
"combinator" 将再次导致代码丑陋:
>>> list(starmap(apply, zip([lambda x: 3 * x, lambda x: 4 + x, lambda x: 5 - x], map(lambda x: [x], [1, 2, 3]))))
[3, 6, 2]
— 同样,Python 的几个基本限制在这里发挥作用:
- laziness 不是内置的,因此不会自动处理,所以如果没有 "forcing" 使用
list()
的星图,你不会得到 "normal" 列表;
- apply不是
(a -> b) -> a -> b
而是(a1 -> a2 -> ... -> aN -> b) -> (a1, a2, ..., aN) -> b
,所以需要用[]
包裹列表元素,使用starmap
而不是正常的map
;这也是缺乏柯里化的结果;
- lambda 语法冗长,因为 Guido 个人偏好反对 lambda,
map
、reduce
等;
具有讽刺意味的是(因为列表理解是 Python 从 Haskell 等语言中借用的东西),我可能会用两种语言编写类似的代码:
# Python
for xsquared in [x**2 for x in range(1, 21) if x % 2 == 0]:
print(xsquared)
# legal, but not idiomatic; you don't construct a list just
# to throw it away.
# map(print, [x**2 for x in range(1, 21) if x % 2 == 0])
和
-- Haskell
main = (mapM_ print) [ x^2 | x <- [1..20], x `mod` 2 == 0 ]
或每个更简短:
# Python
for xsquared in [x**2 for x in range(2, 21, 2)]:
print(xsquared)
-- Haskell
main = (mapM_ print) [x^2 | x <- [2,4..20]]
Python 中的函数比 Haskell 中的函数更难编写。 Haskell 函数有一个参数,return 有一个值。鉴于 f
和 g
的定义类型签名,编译器很容易检查 f . g
是否有意义。然而,Python 没有这样的类型签名(即使在 3.5 中,类型提示也是可选的,并且仅在静态分析期间使用,而不是在运行时使用)。
此外,Python 函数可以接受任意数量的参数(无柯里化),并且元组是可变长度的,而不是固定长度的。假设 g
return 是一个元组。 f ∘ g
(我个人对组合运算符的选择是否应该被采用,并且允许使用 Unicode 运算符)是否应该等同于 f(g(...))
或 f(*g(...))
?两者都有意义,并指向两种不同类型的组合的 "need"。如果 g
的 return 值对于 f
来说太多或太少怎么办? f
的关键字参数怎么样?它们应该取自 return 由 g
编辑的字典吗?在 Python.
中定义看似简单的操作变得相当复杂
还有一件事我可能完全错了。我的印象是 Python 中的每个函数都被编译为一段不同的代码,而 Haskell 可以为每个组合编译优化代码,因此 f . g
不只是天真地转换为\x -> f (g x)
。至少在Python,
def f(x):
return x + 5
def g(x):
return 3 * x
这是编译器可以为 f∘g
生成的内容
def fg(x):
return f(g(x))
这将远低于我所理解的 Haskell 编译器生成的等效内容:
def fg(x):
return 3*x + 5
在Haskell中我会写:
main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
in Python 我将不得不使用许多括号或无用的变量...在 Python 中是否有类似 .
和 $
的东西?
(我不熟悉 Haskell,但如果我正确理解您的代码片段...)
您可以使用列表理解来执行过滤和取幂。
[i**2 for i in range(1,21) if i%2 == 0]
因为 map 函数 returns 可迭代的列表和过滤器也可以嵌套它们 -
map(function1, (filter(function2,list)))
有关更多信息,我建议您阅读 map function documentation also filter function documentation
对于这种情况,您最好使用@CoryKramer 所说的列表理解。
要在 Python 中应用部分应用程序,您应该使用 functools.partial
,就像这样
from functools import partial
def compose(func1, *func2):
return func1 if not func2 else lambda x: func1(compose(*func2)(x))
myMap = partial(map, lambda x: x**2)
myFilter = partial(filter, lambda x: x%2 == 0)
myFunction = compose(myMap, myFilter)
myFunction(range(20))
我会 使用任何可用的惯用 Python 工具,例如其他人指出的列表理解,而不是试图假装你在写Haskell,但如果你真的必须,你甚至可以在 Python:
中使用compose
组合函数
# this is essentially just foldr (or right `reduce`) specialised on `compose2`
def compose(*args):
ret = identity
for f in reversed(args):
ret = compose2(f, ret)
return ret
def identity(x): return x
def compose2(f, g): return lambda x: f(g(x))
你可以这样使用:
from functools import partial
# equiv. of: map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
这确实有效:
>>> compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
... 但是 如您所见,Python 缺少某些概念,例如柯里化和任意可定义的中缀运算符,因此即使在语义上,上面的片段代码与 Haskell 片段等效(甚至相同),它读起来非常糟糕。
至于 $
运算符: 它在 Python 中几乎没有关系 — 它在 Haskell 中的主要目的与运算符优先级有关,这在 Python 中不是问题,因为大多数时候你不能真正使用运算符,而且所有内置运算符都有预定义的优先级。
而 $
还可以用作 Haskell 中的高阶函数:
zipWith ($) [(3*), (4+), (5-)] [1,2,3]
...在 Python 中复制它及其(已弃用)apply
"combinator" 将再次导致代码丑陋:
>>> list(starmap(apply, zip([lambda x: 3 * x, lambda x: 4 + x, lambda x: 5 - x], map(lambda x: [x], [1, 2, 3]))))
[3, 6, 2]
— 同样,Python 的几个基本限制在这里发挥作用:
- laziness 不是内置的,因此不会自动处理,所以如果没有 "forcing" 使用
list()
的星图,你不会得到 "normal" 列表; - apply不是
(a -> b) -> a -> b
而是(a1 -> a2 -> ... -> aN -> b) -> (a1, a2, ..., aN) -> b
,所以需要用[]
包裹列表元素,使用starmap
而不是正常的map
;这也是缺乏柯里化的结果; - lambda 语法冗长,因为 Guido 个人偏好反对 lambda,
map
、reduce
等;
具有讽刺意味的是(因为列表理解是 Python 从 Haskell 等语言中借用的东西),我可能会用两种语言编写类似的代码:
# Python
for xsquared in [x**2 for x in range(1, 21) if x % 2 == 0]:
print(xsquared)
# legal, but not idiomatic; you don't construct a list just
# to throw it away.
# map(print, [x**2 for x in range(1, 21) if x % 2 == 0])
和
-- Haskell
main = (mapM_ print) [ x^2 | x <- [1..20], x `mod` 2 == 0 ]
或每个更简短:
# Python
for xsquared in [x**2 for x in range(2, 21, 2)]:
print(xsquared)
-- Haskell
main = (mapM_ print) [x^2 | x <- [2,4..20]]
Python 中的函数比 Haskell 中的函数更难编写。 Haskell 函数有一个参数,return 有一个值。鉴于 f
和 g
的定义类型签名,编译器很容易检查 f . g
是否有意义。然而,Python 没有这样的类型签名(即使在 3.5 中,类型提示也是可选的,并且仅在静态分析期间使用,而不是在运行时使用)。
此外,Python 函数可以接受任意数量的参数(无柯里化),并且元组是可变长度的,而不是固定长度的。假设 g
return 是一个元组。 f ∘ g
(我个人对组合运算符的选择是否应该被采用,并且允许使用 Unicode 运算符)是否应该等同于 f(g(...))
或 f(*g(...))
?两者都有意义,并指向两种不同类型的组合的 "need"。如果 g
的 return 值对于 f
来说太多或太少怎么办? f
的关键字参数怎么样?它们应该取自 return 由 g
编辑的字典吗?在 Python.
还有一件事我可能完全错了。我的印象是 Python 中的每个函数都被编译为一段不同的代码,而 Haskell 可以为每个组合编译优化代码,因此 f . g
不只是天真地转换为\x -> f (g x)
。至少在Python,
def f(x):
return x + 5
def g(x):
return 3 * x
这是编译器可以为 f∘g
def fg(x):
return f(g(x))
这将远低于我所理解的 Haskell 编译器生成的等效内容:
def fg(x):
return 3*x + 5