在下面的函数中,我想知道编译器是否足够聪明,x
可以计算出保持不变,还是会为列表中的每个项目计算列表的头部?(我正在使用 GHC)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
在下面的函数中,我想知道编译器是否足够聪明,x
可以计算出保持不变,还是会为列表中的每个项目计算列表的头部?(我正在使用 GHC)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
GHC 中“where”的语义是为“x”分配一个闭包并在所有用途之间共享。将为函数 (== 'x') 生成一个新的闭包,优化器会将其浮出,因此每次遍历只生成一次。
要查看生成的确切代码,请检查核心(例如通过 ghc-core)。GHC 将代码优化为:
M.allSame a eq xs =
all
(let
ds =
case xs of
[] -> error "bad head"
x : _-> x
in
\y -> x == y
) xs
如果性能是一个问题,请考虑使用向量,因为单独的遍历将融合,消除递归。
我认为 Haskell 只会评估需要什么:所以它正在寻找x
并在 - 子句中找到它where
。然后我认为它计算x
一次并执行all
.
如果你想测试它,你可以编写一个myall
像 in 一样进行递归的函数all (==x)
,但本质上只是打印出比较元素。所以你会看到,如果你每次都得到一个新的论点,或者它每次都保持不变。
这是一个测试这个的小函数:myall
只收集第一个参数并将其放入列表中。
myall x [] = [x]
myall x xs = x:(myall x (tail xs))
test xs = myall (x) xs where x = head xs
如果您调用test [1,2,3]
,您将看到结果为[1,1,1,1]
,即首先x
评估为1
,然后myall
评估。