【问题标题】:Laziness of Haskell and list comprehension. Completion conditionHaskell 的惰性和列表理解。完成条件
【发布时间】:2021-11-06 20:03:47
【问题描述】:

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

请说明此功能何时结束?

Haskell 会先遍历整个列表然后检查null,还是当一个元素出现时,他会检查null 并完成迭代?如果是第一个选项,那怎么能更懒呢?

【问题讨论】:

    标签: haskell list-comprehension


    【解决方案1】:

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

    null 检查它是否为空列表,并为空列表 [] 返回 True,为 (:) 数据构造函数返回 False。 “老”null is implemented as [src]

    null                    :: [a] -> Bool
    null []                 =  True
    null (_:_)              =  False
    

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

    “新”null is implemented as [src]:

    null :: t a -> Bool
    null = foldr (\_ _ -> False) True
    

    foldr for a list is implemented as [src]:

    foldr k z = go
              where
                go []     = z
                go (y:ys) = y `k` go ys
    

    因此它还将简单地检查外部数据构造函数是空列表[] 还是“缺点”(:),因为在这种情况下k 返回True 并且对参数不感兴趣。

    列表推导也是惰性的:它们只会在必要时评估外部数据构造函数,并在必要时构造头部和尾部。你的列表理解是:

    concatMap (\x -> if x == s1 then [x] else []) s2
    

    如果它因此必须评估列表的外部数据构造函数,它将遍历s2,如果x == s1 然后它将生成作为外部数据构造函数的“cons”,然后null 可以使用它判断列表是否为空。

    因此,这意味着从它从 s2 找到元素 x 的那一刻起,它就会返回 False

    【讨论】: