在下面的函数中,我想知道编译器是否足够聪明地确定x
将保持不变,还是计算列表中每个项目的列表头部? (我正在使用GHC)
allSame :: Eq a => [a] -> Bool
allSame xs = all (==x) xs where x = head xs
答案 0 :(得分:11)
GHC中'where'的语义是单个闭包将被分配给'x'并在所有用途中共享。将生成一个用于函数(=='x')的新闭包,优化器将浮出它,这样它每次遍历只生成一次。
要确切了解生成的代码,请检查Core(例如,通过ghc-core)。 GHC将代码优化为:
M.allSame a eq xs =
all
(let
ds =
case xs of
[] -> error "bad head"
x : _-> x
in
\y -> x == y
) xs
如果需要考虑性能,请考虑使用向量,因为单独的遍历将融合,从而消除递归。
答案 1 :(得分:0)
我认为Haskell只会评估所需的内容:所以它正在寻找x
并在where
- 子句中找到它。然后我认为它会计算x
一次并执行all
。
如果你想测试它,你可以编写一个函数myall
来执行像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