【问题标题】:Beginner doubts on List Comprehension Haskell初学者对列表理解 Haskell 的疑惑
【发布时间】:2017-08-30 03:10:51
【问题描述】:

我目前正在解决 Wiki 上的 99 个 Haskell 问题。话虽如此,我正在做题号 26 的问题,它需要一个程序,该程序可以用给定集合中的 n 个元素编写所有组合。我用很多功能完成了我的实现,程序看起来是正确的。但我的问题不是来自这个,而是来自 Haskell Wiki 的解决方案之一

combinations :: Int -> [a] -> [[a]]
combinations 0 _  = [ [] ]
combinations n xs = [ y:ys | y:xs' <- tails xs
                           , ys <- combinations (n-1) xs']

我知道 y:xs 已针对函数 tails 给出的每个列表进行解析,从而生成可能值的列表,但我不明白 ys 的值是如何这样做是为了让y:ys 给我所有正确的解决方案。

我还有一个更重要的问题是为什么这两个表达式有不同的结果?

[xs | y:xs <- tails [1,2,3]]
[[2,3],[3],[]]

[xs | y:xs <- tails [1,2,3], z <- xs]
[[2,3],[2,3],[3]]

它们可能没有任何实际用途,但它们为我展示了一个 Haskell 初学者,一种非常奇怪的行为。为什么申请z &lt;- xs会改变xs的值?

【问题讨论】:

  • 如果您期望最后两个表达式具有相同的值,那么这是由于误解了列表推导的实际含义。你没有说你的理解是什么(甚至你期望表达式的输出是什么)所以很难看出它是如何被纠正的(注意:最后一个表达式相当于concatMap (\ys -&gt; case ys of { y:xs -&gt; concatMap (\z -&gt; [xs]) xs; [] -&gt; [] }) (tails [1,2,3])
  • 我认为 Haskell 中的列表理解是指守卫之前的表达式给出的一组值,这些值尊重守卫右侧给出的约束。我的想法错了吗?因为根据这个想法,应用 z
  • 您模糊的直觉在某些情况下可能是准确的,但正如您所见,它实际上并不对应于列表推导的语义。有关列表推导的语义,请参阅 here。您可以使用语义将您的表达式转换为使用常规函数定义的表达式,然后说服自己这两个表达式不相等。
  • xs 的值不会改变,但列表推导式为您提供了一个 list 值,而不是一个集合。特别是,如果您在右侧有嵌套生成器,则重复值是可能的。

标签: list haskell list-comprehension


【解决方案1】:

由于comprehension desugaring rules,它们有不同的结果。如果我们有

[ expression | a <- as, b <- bs ]

那么我们会得到

concatMap (\a -> concatMap (\b -> [expression]) bs) as

即使b 没有在expression 中使用,我们仍然会得到更多元素。例如concatMap (const [1]) [1..10] 将导致replicate 10 1。因此,如果您在理解中添加另一个列表,您会更改 concatMap 的数量,因此也会更改输出的数量(除非列表只包含一个元素):

[a | a <- [1..10]]             -- results in [1..10]
[a | a <- [1..10], _ <- [1,2]] -- results in [1,1,2,2,3,3,4,4,...,9,9,10,10]
[a | _ <- [1,2], a <- [1..10]] -- results in [1..10] ++ [1..10]

【讨论】:

    【解决方案2】:

    代码

    combinations n xs = [ y:ys | y:xs' <- tails xs
                               , ys <- combinations (n-1) xs']
    

    读作:从xs 中选择n 元素,在xs 中选择任何y,并将y 之后的元素尾部命名为xs'。然后递归地从xs' 中选择n-1 元素,将任何此类选择命名为ys。结果由所有可以通过这种方式生成的y:ys组成。

    说服自己该算法确实采用了所有可能的选择(准确地说:所有选择尊重原始列表 xs 给出的排序)。

    请注意一些正确处理的极端情况。 首先,在tails xs 中,我们还找到了空尾[],但由于它不是y:xs' 的形式,因此被忽略了。 然后,尾部xs' 可能比n-1 元素短。在这种情况下,ys &lt;- combinations (n-1) xs' 将为ys 找到零选择,因此不会为此xs' 生成“输出”。

    最后,关于你的最后一个问题。考虑这个例子:

    [ 42 | x <- [1,2,3 ] ] == [ 42, 42, 42 ]
    

    x 的值不会影响42 的值,因为42 是常数,但它确实会影响其多重性。这与命令式循环 for x in [1,2,3]: print(42) 非常相似,它会多次生成输出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-07
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 2015-07-30
      • 1970-01-01
      • 2023-03-05
      相关资源
      最近更新 更多