foo s1 s2 = null ([x | x <- s2, x == s1])
请解释一下这个功能什么时候结束?
Haskell 会首先遍历整个列表,然后检查null,还是当出现一个元素时,他会检查null并完成迭代吗?如果是第一种选择,那么怎样才能让它变得更加懒惰呢?
Haskell 首先会遍历整个列表,然后检查
null.
不。null检查它是否为空列表,并返回True空列表[]和数据构造False函数(:)。“旧”null被实现为 [src]:
Run Code Online (Sandbox Code Playgroud)null :: [a] -> Bool null [] = True null (_:_) = False
因此,它会将列表理解评估为头范式(HNF),一旦它知道外部数据构造函数,它就可以返回True或False:它不会检查第一个元素是什么,所以如果这是一个昂贵的表达式,它不会浪费那个也有时间。
“新”null被实现为 [src]:
Run Code Online (Sandbox Code Playgroud)null :: t a -> Bool null = foldr (\_ _ -> False) True
Run Code Online (Sandbox Code Playgroud)foldr k z = go where go [] = z go (y:ys) = y `k` go ys
因此,它也将简单地检查外部数据构造函数是否是空列表[]或“cons” (:),因为在这种情况下k返回True并且对参数不感兴趣。
列表推导式也是惰性的:它们只会在必要时评估外部数据构造函数,并在必要时构造头部和尾部。您的列表理解被简化为:
concatMap (\x -> if x == s1 then [x] else []) s2
Run Code Online (Sandbox Code Playgroud)
因此,如果它必须评估列表的外部数据构造函数,它将迭代s2,如果x == s1那么它将生成“cons”作为外部数据构造函数,然后null可以使用它来确定列表是否为空。
x因此,这意味着从它从s2where找到元素的那一刻起x == s1,它将返回False。