Haskell函数与where有关

Haskell函数与where有关,haskell,Haskell,我是Haskell的新手,我不明白在函数中定义where节时会发生什么 例如,在以下函数中 f x = y + y where y = product [0..x] 我不明白y是否只被product[0..x]替换并计算两次,或者product[0..x]计算一次,其结果保存在一个名为y的变量中,然后求和 如果计算两次,效率不是很低吗?这会将一个值绑定到名称y,然后在定义中使用它两次。你的直觉是正确的,如果你把它定义为 f x = product [0..x] + product [0

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

例如,在以下函数中

f x = y + y
    where y = product [0..x]
我不明白
y
是否只被
product[0..x]
替换并计算两次,或者
product[0..x]
计算一次,其结果保存在一个名为
y
的变量中,然后求和


如果计算两次,效率不是很低吗?

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

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

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

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]
进入

和以前一样执行它,节省一些计算。一个优化编译器可以四处寻找这样的机会,但它必须克制自己!很容易让编译器生成的代码在提升这样的公共子表达式时表现更差(空间泄漏)

出于这个原因,虽然GHC将使用您直接编写的名称来避免重复计算,但它不太可能单独消除常见的子表达式


如有疑问,请使用
let
where

它不会计算两次。Haskell语义没有说明它是计算一次还是两次,但我知道的所有实现都会计算一次。通常我希望ghc做更多的CSE。对于某些事情,比如基本值,这样做是无害的。如果有疑问,请使用
案例
:)。然而,在这种情况下,我相信严格的分析应该会得出同样的结果。如果您必须确定。@d如果您想强制求值,您必须在本例中以不明显的方式使用
case
。@augustss,您在本上下文中是对的。我的评论意在提出一个更广泛的背景。
let y = product [0..x] in y + y