Haskell的功能在哪里

时间:2014-03-20 02:41:44

标签: haskell

我是Haskell的新手,我不明白在函数中定义where部分时会发生什么。

例如,在以下函数中

f x = y + y
    where y = product [0..x]

我不明白y是否仅被product [0..x]替换并计算两次,或者product [0..x]计算一次,其结果保存在类似{{{}的变量中1}}然后再做总和。

如果计算两次会不会效率低下?

2 个答案:

答案 0 :(得分:7)

Haskell是纯粹的,如果你想在任何你喜欢的地方内联y那么值是相同的

y + y where y = product [0..x]    ==    product [0..x] + product [0..x]

但是如果需要,Haskell允许其实现选择更快的执行路径。特别是,Haskell允许一个懒惰(不同于Haskell 要求其实现的“非严格”语义)计算方法

y + y where y = product [0..x]    -- where-let transform
==
let y = product [0..x] in y + y   -- store 'y' on heap
==
{y = THUNK} y + y                 -- (+) forces its arguments
                                  -- let's assume x = 6
==
{y = product [0..6]} y + y        -- we still need `y`, so eval
==
{y = 0} y + y                     -- replace
==
0 + 0
==
0

正如您所看到的,实现此代码的一种方法是将y堆中的值作为THUNK,延迟值,然后根据需要计算并使用最终计算价值在需要y的地方。这被称为惰性图缩减语义,而且确实是GHC实现的。

请注意,GHC在技术上可以做相反的事情,转换

product [0..x] + product [0..x]

let y = product [0..x] in y + y

并像以前一样执行它,保存一些计算。优化编译器可以寻找这样的机会,但它必须克制!在解除像这样的常见子表达式时,让编译器生成的代码执行得更糟(空间泄漏)非常容易。

因此,虽然GHC将使用您直接编写的名称来保存重复计算,但它不可能单独消除常见的子表达式。

如有疑问,请使用letwhere

答案 1 :(得分:3)

这会将值绑定到名称y,然后在定义中使用它两次。你的直觉是正确的,计算它两次是低效的,如果你把它定义为

f x = product [0..x] + product [0..x]

然而,GHC可能会对此进行优化,但可能没有-O2。然而,Haven没有测试过这个理论。 Apparently not.