hdw*_*dw3 5 performance haskell arguments function
我已经开始学习 Haskell 并且我已经读到 Haskell 中的每个函数都只接受一个参数,我无法理解 Haskell 引擎盖下发生的魔法使之成为可能,我想知道它是否有效。
>:t (+)
(+) :: Num a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)
上面的签名意味着(+)函数接受一个Num然后返回另一个接受一个Num并返回一个的函数Num
示例 1相对简单,但我开始想知道当函数稍微复杂一些时会发生什么。
为了这个例子,我编写了一个zipWith函数并以两种方式执行它,一次传递一个参数,一次传递所有参数。
zipwithCustom f (x:xs) (y:ys) = f x y : zipwithCustom f xs ys
zipwithCustom _ _ _ = []
zipWithAdd = zipwithCustom (+)
zipWithAddTo123 = zipWithAdd [1,2,3]
test1 = zipWithAddTo123 [1,1,1]
test2 = zipwithCustom (+) [1,2,3] [1,1,1]
>test1
[2,3,4]
>test2
[2,3,4]
Run Code Online (Sandbox Code Playgroud)
test1和操作而言,这些场景是否有任何不同test2(除了场景_1 可能需要更多内存,因为它需要保存zipWithAdd和zipWithAdd123)[1,2,3],然后过[1,1,1]我意识到我在一篇文章中提出了很多问题,但我相信这些问题是相互关联的,将帮助我(以及其他不熟悉 Haskell 的人)更好地了解 Haskell 中实际发生的事情,从而使这两种情况都成为可能。
Dan*_*ner 10
您询问“Haskell”,但 Haskell 语言规范并不关心这些细节。选择如何进行评估取决于实现 - 规范唯一说明的是评估结果应该是什么,并小心避免给出必须用于计算该结果的算法。所以在这个答案中,我将讨论 GHC,实际上,它是唯一现存的实现。
对于 (3) 和 (4),答案很简单:无论您一次应用zipWithCustom一个参数还是一次应用所有参数,迭代模式都完全相同。(并且该迭代模式是一次迭代两个列表。)
不幸的是,(1) 和 (2) 的答案很复杂。
起点是以下简单算法:
然而,这种算法有点糟糕。这意味着如果你有一个 7 个参数的函数,你分配了 7 个数据结构,当你使用一个参数时,你可能必须沿着一个 7 长的指针链来找到它。总的。所以 GHC 做了一些更聪明的事情。它以一种特殊的方式使用你的程序的语法:如果你将一个函数应用于多个参数,它只会为该应用程序生成一个闭包,字段与参数一样多。
(嗯...这可能是不完全正确实际上,它跟踪,元数的语法方式的用于将左侧的参数个数再定义-每一个功能的=。如果你申请的时候被定义功能的标志。一个函数的参数比它的数量多,你可能会得到多个闭包或其他东西,我不确定。)
所以这非常好,因此您可能会认为test1与test2. 你是对的......当优化器没有打开时。
但是 GHC 也做了很多优化工作,其中之一就是注意“小”定义并将它们内联。几乎可以肯定,打开优化后,您的zipWithAdd和zipWithAddTo123都将在使用它们的任何地方内联,我们将回到只分配一个闭包的情况。
希望这个解释能让你自己回答问题 (1) 和 (2),但以防万一,这里有明确的答案:
- 一次传递一个参数是否与一次传递所有参数一样有效?
也许。一次传递一个参数可能会通过内联转换为一次传递所有参数,然后当然它们将是相同的。在没有这种优化的情况下,与一次传递所有参数相比,一次传递一个参数会带来(非常轻微的)性能损失。
- 在这些情况中的哪Haskell是实际上做计算方面有什么不同
test1和test2?
test1并且test2几乎肯定会被编译为相同的代码——甚至可能只编译其中一个,另一个是它的别名。
如果你想阅读更多关于实现中的想法,Spineless Tagless G-machine论文比它的标题所暗示的要平易近人得多,只是有点过时了。
| 归档时间: |
|
| 查看次数: |
165 次 |
| 最近记录: |