如果我调用以下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..])
答案 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