Haskell 的惰性和列表理解。竣工条件

1 haskell list-comprehension

foo s1 s2 = null ([x | x <- s2, x == s1])

请解释一下这个功能什么时候结束?

Haskell 会首先遍历整个列表,然后检查null,还是当出现一个元素时,他会检查null并完成迭代吗?如果是第一种选择,那么怎样才能让它变得更加懒惰呢?

Wil*_*sem 6

Haskell 首先会遍历整个列表,然后检查null.

null检查它是否为空列表,并返回True空列表[]和数据构造False函数(:)。“旧”null被实现为 [src]

null                    :: [a] -> Bool
null []                 =  True
null (_:_)              =  False
Run Code Online (Sandbox Code Playgroud)

因此,它会将列表理解评估为头范式(HNF),一旦它知道外部数据构造函数,它就可以返回TrueFalse:它不会检查第一个元素是什么,所以如果这是一个昂贵的表达式,它不会浪费那个也有时间。

“新”null被实现为 [src]

null :: t a -> Bool
null = foldr (\_ _ -> False) True
Run Code Online (Sandbox Code Playgroud)

其中foldr列表的实现为 [src]

foldr k z = go
          where
            go []     = z
            go (y:ys) = y `k` go ys
Run Code Online (Sandbox Code Playgroud)

因此,它也将简单地检查外部数据构造函数是否是空列表[]或“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