【问题标题】:How can I "break" a list comprehension in Haskell?如何在 Haskell 中“打破”列表理解?
【发布时间】:2020-01-28 08:40:10
【问题描述】:

为了准备在 Haskell 中的函数式编程考试,我正在重写递归函数以列出推导(不使用 map 和 filter 等高阶函数),这可能是考试中的一项任务。

我重构了我写的这个函数,作为一个练习的解决方案,它接受一个列表和一个元素,如果该元素尚未包含在列表中,则附加该元素

noDupsApp :: Eq a => [a] -> a -> [a]
noDupsApp [] y = [y]
noDupsApp [x] y = if x == y then [x] else [x,y]
noDupsApp (x:xs) y
    | x == y = x:xs
    | otherwise = [x] ++ noDupsApp xs y

noDupsAppComp :: Eq a => [a] -> a -> [a]
noDupsAppComp xs y 
   | [x | x <- xs, x /= y] == xs = xs ++ [y]
   | otherwise = xs

据我所知,这似乎确实有效,但在考虑解决方案时,我问自己是否有办法在 Haskell 中“中断”/“返回”一个函数。如果有这样的选择,我可以在两个元素相同时立即中断,然后将生成的列表与“xs”进行比较,这将提高效率(如果包含“y”而不是最后)。类似问题的其他答案也建议使用条件,但这也会导致整个列表的迭代。

有没有办法打破列表推导式(或者用一个更简单的方法来编写这个函数,用一个列表comp,但不使用高阶函数)?

【问题讨论】:

  • 你看过elem吗?它检查一个元素是否在列表中。

标签: haskell


【解决方案1】:

当找到第一个 y 时,您的代码 [x | x &lt;- xs, x /= y] == xs 已经“中断”。当第二个列表有一个y 时,第一个列表将产生一个不是y 的元素。这将立即从==“中断”,提前返回False

这是由于 == 的惰性实现,它扫描两个列表并在找到第一个差异后立即停止。

作为替代方法,您可以检查是否有[x | x &lt;- xs, x == y] == [],而不是检查[x | x &lt;- xs, x /= y] == xs。这也提前“中断”,相当于y `notElem` xs(也提前中断)。

【讨论】:

  • 感谢您的回答。是否有“打破”列表理解的通用方法,或者使用条件是首选方法?
  • @YeuhYeuh 没有通用的“破解”方法。列表通常是惰性生成的,所以我们要做的就是确保消费者(在这种情况下为==)尽快停止从列表中读取。
【解决方案2】:

列表推导式在需要时映射和过滤所有生成的输入,Haskell 的数学符号不会让您从内部破坏它。

但从外部环境来看,通常不需要进一步处理,runhaskell 会懒惰地跳过不必要的构造和评估。

排除简单的elem y xs 测试,因为它缺乏列表理解和any(==y)xs 近似值,因为它使用了高阶函数,我注意遵循你的风格

xs +? y = if []<[y|x<-xs,x==y] then xs else xs++[y]

成本低至

xs +? y = if or[x==y|x<-xs] then xs else xs++[y]

你可以顺便codegolf进入

xs+?y=xs++[y|and[x/=y|x<-xs]]

提出所有列表推导中最愚蠢的问题

xs+?y=xs++[y|notElem y xs]

还不够。

【讨论】:

    猜你喜欢
    • 2017-01-28
    • 1970-01-01
    • 2018-10-07
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    相关资源
    最近更新 更多