哈斯克尔懒惰的评价

时间:2012-03-14 12:27:38

标签: haskell lazy-evaluation

如果我调用以下Haskell代码

find_first_occurrence :: (Eq a) => a -> [a] -> Int
find_first_occurrence elem list = (snd . head) [x | x <- zip list [0..], fst x == elem]

带参数

'X' "abcdXkjdkljklfjdlfksjdljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"

将建立多少压缩列表[('a',0), ('b',1), ]

更新:

我试图运行

find_first_occurrence 10 [1..]

并几乎立即返回9,所以我猜它至少在简单的情况下使用延迟评估?当我运行

时,答案也会“立即”计算出来
let f n = 100 - n
find_first_occurrence 10 (map f [1..])

3 个答案:

答案 0 :(得分:6)

简短回答:它只会根据您要搜索的元素构建。这意味着只有在最坏的情况下,您才需要构建整个列表,即没有元素满足条件。

答案很长:让我用一对例子解释原因:

ghci> head [a | (a,b) <- zip [1..] [1..], a > 10]
11

在这种情况下,zip应该产生一个无限列表,但是懒惰使Haskell只能构建它(11,11):正如你所看到的,执行没有分歧,但实际上给了我们正确的答案。 / p>

现在,让我考虑另一个问题:

ghci> find_first_occurrence 1 [0, 0, 1 `div` 0, 1]
*** Exception: divide by zero
ghci> find_first_occurrence 1 [0, 1, 1 `div` 0, 0]
1
it :: Int
(0.02 secs, 1577136 bytes)

由于没有构建整个压缩列表,haskell显然甚至不会评估列表中出现的每个表达式,所以当元素在div 1 0之前时,正确评估函数而不引发异常:除零没发生。

答案 1 :(得分:5)

全部。

由于StackOverflow不会让我发布这样一个简短的答案:如果您正在寻找的东西不存在,那么您无法完成比查看整个列表更少的工作。

编辑:问题现在要求更有趣的事情。简短的回答是我们将建立清单:

('a',0):('b',1):('c',2):('d',3):('X',4):<thunk>

(实际上,这个答案有点微妙。你的类型签名使用单态返回类型Int,这在基本上所有操作中都是严格的,所以上面元组中的所有数字都将被完全评估。肯定有Num的实现,但是你会得到更多的东西。)

答案 2 :(得分:5)

您可以通过在此处和之后引入undefined来轻松回答此类问题。在我们的例子中,改变我们的输入就足够了:

find_first_occurrence 'X' ("abcdX" ++ undefined)

你可以看到它产生结果,这意味着它甚至看不到它找到的'X'(否则它会抛出异常)。显然,如果不查看原始列表,就无法构建压缩列表。

另一种(可能不太可靠)分析懒惰的方法是使用trace中的Debug.Trace函数:

> let find_first_occurrence elem list = (snd . head) [x | x <- map (\i -> trace (show i) i) $ zip list [0..], fst x == elem]
> find_first_occurrence 'X' "abcdXkjdkljklfjdlfksjdljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"

打印

('a',0)
('b',1)
('c',2)
('d',3)
('X',4)
4