【问题标题】:Removing elements from [[Int]] list if the numbers appeared before in the same list in Haskell如果数字出现在 Haskell 的同一列表中,则从 [[Int]] 列表中删除元素
【发布时间】:2015-12-07 16:37:34
【问题描述】:

我有这样的事情:

[[1,3,2],[5,6],[3,8],[10,11],[13,6]]

而我想过滤掉第三个也是最后一个列表,让它变成:

[[1,3,2],[5,6],[10,11]]

如果有任何数字“出现在之前”,则逻辑是删除列表(在[3,8] 的情况下,数字3 出现在第一个列表中)。

我只能想到非 FP 方法来做到这一点。我的目标是以“好的方式”学习 Haskell。如何做到这一点?

【问题讨论】:

  • 考虑在调用扫描列表的函数之间传递一组已经看到的数字。 (请记住在调用fold 期间如何传递部分结果)。
  • 我没有心情对此做出正确的回答(而且效率不高),但可能的解决方案是nubBy (\l m -> not . null $ intersect l m)
  • 假设你有一个列表[[1,3,2],[5,6],[3,8],[10,11],[8],[13,6]][8] 也应该是输出的一部分吗?你没有“发出”[3,8],但可以这么说仍然观察到它。
  • @cambraca:read the documentation。当您不知道文档在哪里时,ask Hayoo。当您甚至不知道所需函数的名称时,ask Hoogle
  • @cambraca CommuSoft 的问题不是关于 [3,8] 项目——我们都同意不应该发出它——而是关于稍后出现在他们的示例输入列表中的 [8] 项目。一个更简短的例子是:[[0], [0,1], [1]][[0]][[0], [1]] 上的行为应该是什么?

标签: list haskell filter


【解决方案1】:

这个怎么样:你使用像 Data.HashSet 这样的累加器,然后使用某种过滤器方法,比如:

import Data.HashSet
import Data.Hashable

somefilter :: (Hashable a, Ord a) => [[a]] -> [[a]]
somefilter = sf empty

sf :: (Hashable a, Ord a) => Set a -> [[a]] -> [[a]]
sf hs (x:xs) | any (flip member hs) x = tl
             | otherwise = x : tl
             where tl = sf (foldr insert hs x) xs
sf _ [] = []

然后是演示:

*Main> somefilter [[1,3,2],[5,6],[3,8],[10,11],[13,6]]
[[1,3,2],[5,6],[10,11]]

您当然可以使用其他数据结构(列表、树集等)来存储您迄今为止遇到的值。 HashSet 是一个合理的选择,因为它的查找和插入时间复杂度低。

编辑

显然,对于惰性编程来说,时间复杂度并不容易衡量。如果你打算生成整个列表,时间复杂度可以从documentation of Data.IntMap推导出来。

鉴于元素总数为N(不要与列表的数量混淆),我们考虑Int的位数W 固定(32 或 64),insertlookup 都在恒定时间内完成。这意味着总时间复杂度为O(N)

对于nubBy 解决方案,虽然intersect 的时间复杂度可能在O(mn) 中运行,其中m 是一个列表中的项目数, n 另一个列表中的元素数。这是针对所有以前的列表针对每个列表完成的。所以如果有 O(k) 个列表,时间复杂度是 O(k2 a2) a 是列表中的平均项目数。由于a k=N,因此时间复杂度约为O(N2)

编辑 2

基于your comment,可以重写函数以满足新的规范:

import Data.HashSet
import Data.Hashable

somefilter2 :: (Hashable a, Ord a) => [[a]] -> [[a]]
somefilter2 = sf2 empty

sf2 :: (Hashable a, Ord a) => Set a -> [[a]] -> [[a]]
sf2 hs (x:xs) | any (flip member hs) x = sf2 hs xs
              | otherwise = x : sf2 (foldr insert hs x) xs
sf2 _ [] = []

somefiltersomefilter2的区别:

*Main> somefilter [[1,3,2],[5,6],[3,8],[10,11],[8],[13,6]]
[[1,3,2],[5,6],[10,11]]
*Main> somefilter2 [[1,3,2],[5,6],[3,8],[10,11],[8],[13,6]]
[[1,3,2],[5,6],[10,11],[8]]

只有sf 的递归部分有所不同:现在您只需将元素添加到Set,以防您“发出”一个元素。

【讨论】:

  • 知道这与@leftroundabout 的解决方案在性能方面有何不同吗? (nubBy (\l m -> not . null $ intersect l m))
  • @cambraca:intersectnumBy 没有完全指定,但根据我之前所见,我预计二次时间复杂度,而使用 HashSet 导致线性时间复杂度(见编辑)。
  • @CommuSoft 标准库包括Data.List,其中包含intersectnubBy
  • @CommuSoft 抱歉,如果这是一个愚蠢的问题,我在... => Set a ... 上遇到错误:“不在范围内:类型构造函数或类'Set'”。我需要导入其他东西吗?另外,我使用的是 Data.List,它显然有很多与 Data.HashSet 相似的功能。为什么是这样?两者之间filterunion 有什么区别?我想我不能互换使用它们。
  • @cambraca:你安装了hashmap 包吗?还有另一个包 (unordered-containers) 使用相同的模块名称...
猜你喜欢
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-21
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多