Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 基于共享元素递归合并列表列表_Haskell - Fatal编程技术网

Haskell 基于共享元素递归合并列表列表

Haskell 基于共享元素递归合并列表列表,haskell,Haskell,我不知道我要做的事情的官方技术名称是什么,所以我会尽力解释 给出一个列表: [[2,3,4,5], [1,5,6], [7,8,9]] 我只想合并至少有一个公共元素的列表。基本上是这样的: simUnion :: [[Int]] -> [[Int]] simUnion list = --... --Result -- [[1,2,3,4,5,6], [7,8,9]] 我遇到的问题是在每个元素之间运行匹配过程。基本上,这就像旧的数学课堂题,房间里的每个人都必须与其他人握手。通常,我会使

我不知道我要做的事情的官方技术名称是什么,所以我会尽力解释

给出一个列表:

[[2,3,4,5], [1,5,6], [7,8,9]]
我只想合并至少有一个公共元素的列表。基本上是这样的:

simUnion :: [[Int]] -> [[Int]]
simUnion list = --...

--Result
-- [[1,2,3,4,5,6], [7,8,9]]
我遇到的问题是在每个元素之间运行匹配过程。基本上,这就像旧的数学课堂题,房间里的每个人都必须与其他人握手。通常,我会使用嵌套的for循环来完成这一点,但是如何使用Haskell的递归来实现呢


任何帮助都将是伟大的

如果有有限数量的不同元素,您可以将任务从内到外,并从您的
[[elem]]]
映射元素[[elem]],然后通过下一个算法开始迭代合并元素:

  • 当地图不是空的时候,拿走一把钥匙,把它放到队列中
  • 获取包含从队列中弹出的密钥的所有组
  • 将它们浓缩并放入队列(以及一些累加器中)
  • 如果队列为空,则组结束;从地图上再拿一把钥匙

  • 如果有有限数量的不同元素,您可以将任务从内到外,从您的
    [[elem]]
    中生成一个
    Ord elem=>Map elem[[elem]]
    ,然后通过下一个算法开始迭代合并元素:

  • 当地图不是空的时候,拿走一把钥匙,把它放到队列中
  • 获取包含从队列中弹出的密钥的所有组
  • 将它们浓缩并放入队列(以及一些累加器中)
  • 如果队列为空,则组结束;从地图上再拿一把钥匙
  • 注意:下面的帖子是用识字的哈斯克尔写的。将其另存为
    *.lhs
    ,并将其加载到GHCi中。还要注意,所讨论的算法具有运行时O(n²),并且不是最优的。更好的方法是使用或类似的方法

    首先,如果要将单个列表
    x
    与其余列表
    xs
    分组,让我们考虑一下所需的工具。我们需要从
    xs
    列表中分离出与
    x
    有共同元素的列表,并需要构建此类列表的
    联合。因此,我们应该从
    数据中导入一些函数。List

    > import Data.List (partition, union)
    
    接下来,我们需要检查两个列表是否适合合并:

    > intersects :: Eq a => [a] -> [a] -> Bool
    > intersects xs ys = any (`elem` ys) xs
    
    现在我们手头有了定义
    simUnion
    的所有工具。空的情况很清楚:如果我们没有任何列表,结果也没有任何列表:

    > simUnion :: Eq a => [[a]] -> [[a]]
    > simUnion []     = []
    
    假设我们至少有两个列表。我们取第一个,检查它们是否有与任何其他列表相同的元素。我们可以使用
    分区

    > simUnion (x:xs) = 
    >   let (common, noncommon) = partition (intersects x) xs
    
    现在,
    common::[[a]]
    将只包含至少有一个公共元素的列表。现在可以有两种情况:要么
    common
    为空,要么我们的列表
    x
    xs
    中的任何列表没有共同元素:

    >   in if null common 
    >         then x : simUnion xs
    
    我们在这里忽略了
    不常见的
    ,因为在这种情况下
    xs==不常见的
    。在另一种情况下,我们需要在
    common
    x
    中构建所有列表的并集。这可以通过
    foldr union
    完成。但是,此新列表必须再次在
    simUnion
    中使用,因为它可能有新的交点。例如,在

    simUnion [[1,2], [2,3], [3,4]]
    
    您希望以
    [[1,2,3,4]]
    结束,而不是
    [[1,2,3],[3,4]

    >          else simUnion (foldr union x common : noncommon)
    
    请注意,结果将被取消排序,但作为最后一步,您可以
    映射排序。

    注意:下面的帖子是用识字的Haskell写的。将其另存为
    *.lhs
    ,并将其加载到GHCi中。还要注意,所讨论的算法具有运行时O(n²),并且不是最优的。更好的方法是使用或类似的方法

    首先,如果要将单个列表
    x
    与其余列表
    xs
    分组,让我们考虑一下所需的工具。我们需要从
    xs
    列表中分离出与
    x
    有共同元素的列表,并需要构建此类列表的
    联合。因此,我们应该从
    数据中导入一些函数。List

    > import Data.List (partition, union)
    
    接下来,我们需要检查两个列表是否适合合并:

    > intersects :: Eq a => [a] -> [a] -> Bool
    > intersects xs ys = any (`elem` ys) xs
    
    现在我们手头有了定义
    simUnion
    的所有工具。空的情况很清楚:如果我们没有任何列表,结果也没有任何列表:

    > simUnion :: Eq a => [[a]] -> [[a]]
    > simUnion []     = []
    
    假设我们至少有两个列表。我们取第一个,检查它们是否有与任何其他列表相同的元素。我们可以使用
    分区

    > simUnion (x:xs) = 
    >   let (common, noncommon) = partition (intersects x) xs
    
    现在,
    common::[[a]]
    将只包含至少有一个公共元素的列表。现在可以有两种情况:要么
    common
    为空,要么我们的列表
    x
    xs
    中的任何列表没有共同元素:

    >   in if null common 
    >         then x : simUnion xs
    
    我们在这里忽略了
    不常见的
    ,因为在这种情况下
    xs==不常见的
    。在另一种情况下,我们需要在
    common
    x
    中构建所有列表的并集。这可以通过
    foldr union
    完成。但是,此新列表必须再次在
    simUnion
    中使用,因为它可能有新的交点。例如,在

    simUnion [[1,2], [2,3], [3,4]]
    
    您希望以
    [[1,2,3,4]]
    结束,而不是
    [[1,2,3],[3,4]

    >          else simUnion (foldr union x common : noncommon)
    

    请注意,结果将被取消排序,但作为最后一步,您可以
    映射排序。

    我有两个主要建议:

  • 不要把它看成是递归!相反,自由地使用库实用程序函数
  • 使用适当的数据结构!因为您正在谈论成员资格测试和联合,所以集合(来自
    Data.Set
    模块)听起来似乎是更好的选择
  • 应用这些想法,这里有一个相当简单(尽管可能非常幼稚和次优)的解决方案:

    import Data.Set (Set)
    import qualified Data.Set as Set
    
    simUnion :: Set (Set Int) -> Set (Set Int)
    simUnion sets = Set.map outer sets
      where outer :: Set Int -> Set Int
            outer set = unionMap middle set
                where middle :: Int -> Set Int
                      middle i = unionMap inner sets
                          where inner :: Set Int -> Set Int
                                inner set
                                    | i `Set.member` set = set
                                    | otherwise          = Set.empty
    
    -- | Utility function analogous to the 'concatMap' list function, but
    -- for sets.
    unionMap :: (Ord a, Ord b) => (a -> Set b) -> Set a -> Set b
    unionMap f as = Set.unions (map f (Set.toList as))
    
    现在使用您的示例:

    -- | This evaluates to:
    --
    -- >>> simUnion sampleData
    -- fromList [fromList [1,2,3,4,5,6],fromList [7,8,9]]
    sampleData :: Set (Set Int)
    sampleData = Set.fromList (map Set.fromList sampleData')
        where sampleData' :: [[Int]]
              sampleData' = [[2,3,4,5], [1,5,6], [7,8,9]]
    
    通常我会