当前位置: 首页 > 知识库问答 >
问题:

Haskell中列表理解的效率

钮出野
2023-03-14

我一直在学习Haskell,我做了一些统计函数,这些函数可以用于IntFloat类型的列表
以下是两个示例:

mean :: (Real a, Floating b) => [a] -> b
mean xs = realToFrac (sum xs) / genericLength xs

sumOfSquares :: (Real a, Floating b) => [a] -> b
sumOfSquares xs = [(mean xs - realToFrac x) ^^ 2 | x <- xs]

然而,我现在想知道我是否可以通过以不同的方式编写SumOfSquares来改进运行时。像这样:

sumOfSquares xs = [(m - realToFrac x) ^^ 2 | x <- xs] where m = mean xs

或者像这样:

sumOfSquares xs = [(m - realToFrac x) ^^ 2 | x <- xs, let m = mean xs]

我不知道哈斯克尔在屏幕后面是怎么处理的。在原始版本中,计算机是否为xs中的每个条目计算平均xs?我想Haskell不会在背景中优化理解。我错了吗?

共有1个答案

解念
2023-03-14

脱脂确实会产生不同的代码。这里有三种实现。我将展示如果没有列表理解,它们中的每一个会是什么样子。

-- With no let / where
sumOfSquares xs = [(mean xs - realToFrac x) ^^ 2 | x <- xs]
sumOfSquares xs = map (\x -> (mean xs - realToFrac x) ^^ 2) xs

-- With let inside the comprehension
sumOfSquares xs = [(m - realToFrac x) ^^ 2 | x <- xs, let m = mean xs]
sumOfSquares xs = map (\x -> let m = mean xs in (m - realToFrac x) ^^ 2) xs

-- With where outside the comprehension
sumOfSquares xs = [(m - realToFrac x) ^^ 2 | x <- xs] where m = mean xs
sumOfSquares xs = map (\x -> (m - realToFrac x) ^^ 2) xs where m = mean xs

所以前两者基本相当。如果你有世界上最简单的编译器(或者你关闭优化),你会得到效率相当低的代码,每次迭代都会重新计算平均值。第三种方法完全避免了这种情况,实际上是将计算放在块之外。它不能保证值不会在每一步都被重新计算(Haskell可以自由地计算它选择的东西,只要结果的行为等同于懒惰的计算),但是如果你的编译器不是很聪明,它确实会提高几率。

然而,实际上,GHC(和大多数其他编译器)将执行所谓的浮点优化。基本上,如果它看到一个let块,它可以在重复计算之外移动,它可以自由地这样做,因为这样做不会产生任何可能的副作用。一般来说,GHC将采用以下形式的表达式

\x -> let y = something in f x y

并将其转换为

let y = something in \x -> f x y

这种优化是允许的,只要x某物中不显示为自由变量,并且大多数编译器对此相当聪明,所以您不应该有太多的担心。

关于GHC执行的许多常见优化,有一个极好的StackOverflow答案,这个答案的部分灵感来源于此。如果你想更多地了解Haskell的效率,我建议你去看看。

 类似资料:
  • 假设我们有一个函数sqrt的示例,它为我们提供浮点数的平方根,但不会在负输入时中止。那么sqrt的类型是什么。 我被告知答案是: 然而,我不明白Maybe是做什么的,它仅仅意味着我们可以提供任何东西作为输入,还是意味着其他东西。

  • 问题内容: 我正在玩Go,但是我很难做其他语言中非常简单的事情。 我想重现类似的语法: 在Go中,有什么优雅的方法?我真的很想简化我的代码,尤其是在数组上使用函数时。例如: 非常感谢 问题答案: 有趣的是,Rob Pike 刚刚提出了(18小时前)库过滤器,该过滤器可以满足您的要求: 例如查看Choose() 在这里测试: 正如评论中[]指出的那样,该库的GoDoc指出: 软件包包含实用程序功能,

  • 我在使用Haskell编写的程序中遇到了一些困难。其背后的思想是递归地在一个列表中找到最短的列表并返回该列表。我已经设法把程序写得很好,但我似乎不知道我在其中做错了什么。这些是我尝试编译时得到的错误: 无法将类型“a”与“[[a]]”匹配,“a”是一个严格的类型变量,由类型签名绑定的类型为:seltest::forall a。短时间内。HS:1:13。预期类型:[[[a]]],实际类型:[a] 在

  • 我想知道如何在Haskell中编写一个函数,将一个列表交织成单个列表,例如,如果我有一个名为

  • 问题内容: 语言指南显示没有列表理解的痕迹。 在Swift中完成这项工作的最简洁的方法是什么? 我正在寻找类似的东西: 问题答案: 从Swift 2.x开始,Python样式列表理解有一些简短的等效内容。 Python公式的最直接的改编(读取类似“将转换应用于要过滤的序列”之类的东西)包括将可用于所有s 的和方法链接起来,并从以下位置开始: 请注意,a 是抽象的- 它实际上不会创建您要求的值的完整

  • 语言指南没有显示出列表理解的痕迹。在SWIFT中实现这一点的最简洁的方法是什么?我在找类似于: