Haskell的功能在哪里

Adr*_*ian 2 haskell

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

例如,在以下功能中

f x = y + y
    where y = product [0..x]
Run Code Online (Sandbox Code Playgroud)

我不明白是否y只被替换为product [0..x]和计算两次,或者如果product [0..x]被计算一次并且其结果被保存在类似于被调用的变量中y,然后进行求和.

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

J. *_*son 7

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

y + y where y = product [0..x]    ==    product [0..x] + product [0..x]
Run Code Online (Sandbox Code Playgroud)

但如果需要,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
Run Code Online (Sandbox Code Playgroud)

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

请注意,从技术上讲,GHC可以做相反的事情来进行转换

product [0..x] + product [0..x]
Run Code Online (Sandbox Code Playgroud)

let y = product [0..x] in y + y
Run Code Online (Sandbox Code Playgroud)

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

因此,尽管GHC将使用您直接编写的名称以节省重复计算,但它不可能单独消除常见的子表达式.

如有疑问,请使用let或者where!