Python 什么是一个很好的例子,可以通过列表理解来完成高阶函数的复杂操作?

Python 什么是一个很好的例子,可以通过列表理解来完成高阶函数的复杂操作?,python,haskell,clojure,functional-programming,list-comprehension,Python,Haskell,Clojure,Functional Programming,List Comprehension,我听很多Pythonists说,他们更喜欢列表理解,因为他们可以使用高阶函数(如filter and reduce、和等)完成所有可以完成的事情。因此,这个问题解决了它们:你可以用它们来做什么,这是一个很难用HOFs来做的坚实的例子?在Haskell中,列表理解是条件和函数的“语法糖”(或者可以简单地翻译成do符号,然后一元地去除)。以下是翻译它们的“官方”指南: 因此,由于列表理解可以使用简单的高阶函数机械地、直接地转换为等效代码,因此从定义上讲,没有它们就很难处理它们。答案是没有这样的示例。

我听很多Pythonists说,他们更喜欢列表理解,因为他们可以使用高阶函数(如filter and reduce、等)完成所有可以完成的事情。因此,这个问题解决了它们:你可以用它们来做什么,这是一个很难用HOFs来做的坚实的例子?

在Haskell中,列表理解是条件和函数的“语法糖”(或者可以简单地翻译成do符号,然后一元地去除)。以下是翻译它们的“官方”指南:


因此,由于列表理解可以使用简单的高阶函数机械地、直接地转换为等效代码,因此从定义上讲,没有它们就很难处理它们。

答案是没有这样的示例。使用列表理解所能做的一切都是对高阶函数的机械转换。事实上,Haskell就是这样实现列表理解的:它将它们分解为高阶函数

给出如下列表:

[(x, y) | x <- [1..3], y <- [4..6]]
data Foo = Bar Int | Baz String

getBazs :: [Foo] -> [String]
getBazs = foldr go []
          where go (Baz x) acc = x:acc
                go _       acc =   acc
类似地,如果您放入如下谓词:

[(x, y) | x <- [1..3], y <- [4..6], x + y /= 5]

事实上,这种脱胶是Haskell规范的一部分,您可以找到。

其他的都是正确的;与map、reduce、filter等函数相比,列表理解本身并没有提供更好的序列操作。不过,它们并没有真正解决您的问题,即为什么Python程序员比高阶函数更擅长列表理解

Python提倡它和Python程序员使用它们的原因是,根据语言创建者Guido的说法,列表理解(以及集合理解、dict压缩和生成器表达式)比函数表达式更容易读写。Python的理念是可读性胜过一切

Guido通常不喜欢函数式编程结构,并且对添加
lambda
语法持谨慎态度。这只是风格和品味的问题,而不是表现力或力量的问题。他的观点塑造了Python及其编写方式

要了解更多详细信息,Guido建议从Python 3及更高版本中删除
lambda
map
filter
reduce
。它没有实现(除了删除不再是内置函数的
reduce
),但他在这里陈述了他的理由:

但他总结如下:

filter(p,S)几乎总是写得更清楚,如[x for x in S if p(x)],这有一个巨大的优势,即最常见的用法涉及比较谓词,例如x==42,并且为其定义lambda只需要读者付出更多的努力(加上lambda比列表理解慢)


如前所述,列表理解所能做的一切都可以被分解为高阶函数,但在Python中这样做的很大一部分问题是Python缺乏对可用于Haskell中的
filter
map
和friends的无点编程的支持。这里有一个有点做作的例子,但我想你会明白的

让我们看一下Python代码:

[(x,y)表示zip中的x,y(x范围(20),x范围(20,0,-1)),如果x%2==0和y%2==0]

它所做的只是打印出来:

[(0,20)、(2,18)、(4,16)、(6,14)、(8,12)、(10,10)、(12,8)、(14,6)、(16,4)、(18,2)]

以下是带有过滤器的等效版本:

过滤器(lambda ns:ns[0]%2==0和ns[1]%2==0,zip(xrange(20),xrange(20,0,-1))

我希望你能同意我的看法,那会更难看。如果不定义一个单独的函数,你真的没有什么办法让它变得不那么丑陋

但让我们看看Haskell中的等效版本:

[(x,y) | (x,y) <- zip [0..20] [20,19..0], x `mod` 2 == 0 && y `mod` 2 == 0]
好的,我们必须做一个导入,但是一旦你理解了代码的作用,代码(依我看)就会清晰得多,尽管有些人可能仍然喜欢用
f
来指出,甚至是带过滤器的lambda。在我看来,无点版本更简洁,概念更清晰。但我想说的主要一点是,在Python中,这一点并不是很清楚,因为无法在不引入单独库的情况下部分应用函数,并且缺少组合运算符,因此在Python中,最好选择列表理解而不是映射/过滤器,但是在Haskell中,它可以根据具体问题的不同而采取任何一种方式。

比较

    [[x*x, x*x+x ..] | x <- [2..]]
第一个显然更具可读性。你问的是“棘手”,而不是“不可能”。使用
filter
,没有任何迹象表明我们是在过滤通过或失败给定测试的元素。有了LCs,它在视觉上是明显的


因此,无论何时,只要有LC公式,IMO都会首选它,只是为了它的可读性。Haskell的LC语法特别简洁明了,比Python的IMO(噪音更小)更清晰。不好意思不使用它。:)

我很少使用列表理解,原因正是这个问题。然而,有一种情况我发现它们是唯一简洁的语法:当一个可反驳的模式在
列表的左侧时,理解不能模仿
reduce
问题是向后的吗?你的意思是高阶函数容易理解,而列表理解却很难理解吗?你的问题的介绍似乎提出了完全相反的问题。@Viclib,大多数蟒蛇学家都是初学者。你最好注意专家们说的话。我有时不太明白。人们给《时代》杂志写下了精彩的答案。其他人对这些答案投了赞成票——已经有13票了——这意味着这个问题对社区很有用。然而,没有人愿意对这个问题投一票,然后它就被关闭了…@Viclib你可以让我的票重新打开,然后重新投票
import Data.Function
let f = (&&) `on` (==0) . (`mod` 2)
filter (uncurry f) $ zip [0..20] [20,19..0]
    [[x*x, x*x+x ..] | x <- [2..]]
    map (\x-> map (*x) $ enumFrom x) $ enumFrom 2
data Foo = Bar Int | Baz String

getBazs :: [Foo] -> [String]
getBazs xs = [x | Baz x <- xs]
data Foo = Bar Int | Baz String

getBazs :: [Foo] -> [String]
getBazs = foldr go []
          where go (Baz x) acc = x:acc
                go _       acc =   acc
import Data.Maybe

data Foo = Bar Int | Baz String

getBazs :: [Foo] -> [String]
getBazs = mapMaybe go
          where go (Baz x) = Just x
                go _       = Nothing