【问题标题】:list comprehension unique values列表理解唯一值
【发布时间】:2017-12-29 18:07:00
【问题描述】:

我想知道如何使用 Haskell 列表推导从列表中获取唯一值。因此,如果我输入 [2,4,5,4,4,6,2],它将返回 [2,4,5,6]。

最初我从唯一 (y:ys) = [x | x

【问题讨论】:

  • 不要用列表理解来做这件事,只需使用the nub function。或者,可能更好,根据您的用例,使用 Set 而不是列表。
  • 是的,但我正在尝试在没有内置函数的情况下练习和解决它
  • 在列表理解中仅使用保护子句是不可能的。这些仅限于“本地”视角,一次只查看一个元素。要根据输入的其余部分排除元素,您将需要一些其他构造,例如 nub 或折叠。
  • 如果你想避免库函数,你必须以某种形式重新实现它们。首先定义一个给定xys 的函数,删除列表ys 中所有出现的x。然后使用递归扫描列表并删除重复项。这相当于你自己重写nub

标签: list haskell recursion list-comprehension


【解决方案1】:

你可以用一种懒惰的方式(也许是不必要的聪明),从一些循环推理开始:输入的每个元素都应该出现在输出中,只有当它没有出现在输出中时。

也就是说,对于像[0, 0, 1] 这样的输入列表,应该添加第一个0,但不应该添加第二个0

显然,这样的事情是行不通的:

unique xs = us
  where us = [x | x <- xs, x `notElem` us]

因为它会陷入无限循环,试图测试尚未生成的输出元素。您可以改为将推理更改为:输入的每个元素都应该出现在输出中,前提是它还没有已经出现在输出中。

您可以通过考虑“已经”的含义来直接实现这一点:当前值不得出现在当前索引之前的索引处。

unique xs = catMaybes us
  where
    us =
      [ if Just x `elem` take i us  -- If the element has appeared before here
        then Nothing                -- then don’t include it again
        else Just x                 -- otherwise do include it.
      | (i, x) <- zip [0..] xs      -- (Zip the elements with their indices.)
      ]

所以对于输入列表xs = [0, 0, 1],这将生成xs' = [Just 0, Nothing, Just 1],它会被catMaybes 展平为[0, 1]。使用 QuickCheck 进行测试确认这等同于 nub,并暂停,因为我们在每一步只检查 us 的第一个 take i 元素,确保我们不会检查任何尚未生成的元素。

值得注意的是,和nub一样,输入的长度是O(n2)。

【讨论】:

    【解决方案2】:

    @amalloy 的评论指出,列表推导仅限于“本地”视角,这是这里的关键见解。将nub 写为列表理解是一种明智的方式,但您首先需要改变您的观点。

    在库中遗憾地省略了一个经常有用的函数,该函数用它的上下文来装饰列表的每个元素。

    picks :: [x] -> [([x], x, [x])]
    picks []       = []
    picks (x : xs) = ([], x, xs) : [(x : bs, y, as) | (bs, y, as) <- picks xs]
    

    所以

    picks [1,2,3] =
    [([],1,[2,3]), ([1],2,[3]), ([1,2],3,[])]
    

    列表的每个元素都放在三元组的中间,“之前”元素在其左侧,“之后”元素在其右侧。

    This answer of mine 解释了在某种意义上使picks 成为“标准”操作的深层结构,可从列表的结构派生。但我们不需要这些背景信息来部署它。

    picks 函数准确地为我们提供了编写 nub 作为列表理解所需的上下文信息。我们需要做的就是挑选出没有出现在它们自己的“前列表”中的元素。

    myNub :: Eq x => [x] -> [x]
    myNub xs = [x | (bs, x, as) <- picks xs, not (elem x bs)]
    

    我不承诺此操作的效率,但我确实喜欢将列表推导与额外空间上下文相结合所带来的清晰性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-31
      • 1970-01-01
      • 2016-09-10
      • 2012-01-15
      • 1970-01-01
      • 2019-09-13
      • 2020-04-13
      • 1970-01-01
      相关资源
      最近更新 更多