【问题标题】:How to get a list of tuples and create a list of tuples with lists inside of them如何获取元组列表并创建包含列表的元组列表
【发布时间】:2021-10-28 03:06:05
【问题描述】:

我的目标是接受如下输入:

[(8, P1), (8, P2), (10, P3)]

然后把它变成这样的东西:

[(8, [P1, P2]), (10, P3)]

假设810 之类的数字是数据类型Time(包装和Int),P1P2 是数据类型Person(包装String)。这是我到目前为止所做的:

groupTogether :: [(Time, Person)] -> [(Time, Person)]
groupTogether [] = []
groupTogether ((x, y) : (a, b) : ks)
  | x == a = (x, (y : (b))) : groupTogether ks
  | otherwise = (x, y) : groupTogether ((a, b) : ks)

而且它“有点”有效,但通常输出是 (8, Name1Name2) 而不是 (8, [Name1, Name1])。而且我只是不知道一旦列表中只有一个元素,该函数应该做什么。它说缺少“详尽的模式”。我究竟做错了什么?如果我尝试使用 : 将元素放在一起,代码将无法运行。

【问题讨论】:

  • 如果输入是[(8, P1), (10, P3), (8, P2)],这意味着结果是[(8, [P1]), (10, [P3]), (8, [P2])]还是[(8, [P1, P2]), (10, [P3])]
  • @WillemVanOnsem 输出应该是 [(8, [P1, P2]), (10, [P3])] 当元组的第一个元素匹配时将字符串组合成一个列表。
  • [(8, P1), (10, P3), (8, P2)] 应该变成什么?
  • [(8, P2), (8, P1), (8, P2)]怎么样

标签: haskell recursion ghci


【解决方案1】:

这个怎么样?

import Data.List
import Data.Function

data P = P1 | P2 | P3 deriving Show

x = [(8, P1), (8, P2), (10, P3)]

fun list = let lists = groupBy ((==) `on` fst) x -- [[(8,P1),(8,P2)],[(10,P3)]]
               nums  = map (fst . head) lists    -- [8,10]
               ps    = (map . map) snd lists     -- [[P1,P2],[P3]]
           in zip nums ps                        -- [(8,[P1,P2]),(10,[P3])]      

lists 中,我根据数字的相等性对项目进行了分组,在nums 中,我提取了每个组中所有项目共有的数字,在ps 中,我提取了那些@987654326 @ 事物,无论它们是什么,通过 (map . map) . snd 应用 snd 通过两个功能层。

请注意,如果通常x 中相同数量的项目不一定相邻(在您的示例中它们是相邻的),您可能希望在使用groupBy 之前使用适当的排序算法来sort 列表,如下面的 cmets 中所建议的那样。


关于你想要的输出

[(8, [P1, P2]), (10, P3)]

这根本不可能获得,因为在 Haskell 中,列表的所有元素都具有相同的类型,但 (8, [P1, P2])(10, P3) 必须具有不同的类型,即 (Time, [Person])(Time, Person)。您的问题下的一些 cmets 已经暗示了这一点,但您尚未更正您的问题(您应该)。在我的回答中,我假设您打算写 [(8, [P1, P2]), (10, [P3])]

关于你的尝试

groupTogether :: [(Time, Person)] -> [(Time, Person)]
groupTogether [] = []
groupTogether ((x, y) : (a, b) : ks)
  | x == a = (x, (y : (b))) : groupTogether ks
  | otherwise = (x, y) : groupTogether ((a, b) : ks)

它有几个语法问题:

  • 签名错误,因为它表明输出与输入具有相同的类型;这当然是可能的,但不能反映(校正后的)所需输出;可能你打算写groupTogether :: [(Time, Person)] -> [(Time, [Person])]
  • groupTogether [] = [] 处理空列表输入,而groupTogether ((x, y) : (a, b) : ks) 处理两个或更多元素的列表输入,但是没有办法处理单例列表,这正是“exaustive pattern missing”错误所暗示的;
  • 因为yb具有相同的类型,Person,表达式y : (b)是不正确的,因为它等价于y:b,而:想要一个a在左边和一个@ 987654349@在右边;你可能想把它改成y:[b],或者[y,b]
  • 以类似的方式,yotherwise 的情况下应该是[y]

但是,即使您应用了上述更正,仍然会有一些不太正确的地方。看看这个:

groupTogether ((x, y) : (a, b) : ks)
  | x == a = (x, y : [b]) : groupTogether ks

您正在模式匹配列表中的前两对并将它们放在一对中,但是如果ks 中的第一对有另一个a 作为它的第一个元素怎么办?您将其留在ks,而不是将其与其他两个分组。从您的问题文本来看,这要么是错误的,要么是不清楚的,在这种情况下,您应该改进它。

【讨论】:

  • 值得一提的是groupBy 只对相邻项进行分组。从 OP 的问题中不清楚是否假定相似的项目是相邻的(它们在非常小的给定示例中,因此这取决于 OP 的“输入之类”的含义)。如果不假定项目是相邻的,一个简单的解决方案是首先对元组列表进行排序(按元组的第一个元素排序,最好使用稳定排序)。
  • @Stef Haskell 的图书馆 sort is stable,仅供参考。
  • @WillNess 感谢您的精确;我对 Haskell 不是很熟悉。尽管如此,强调“按元组的第一个元素进行稳定排序”很重要。例如,“按元组的字典顺序进行的稳定排序”尤其是按元组的第一个元素进行的排序,但它会是一个不稳定的排序。
  • @WillNess 是的。尽管它很简单,但值得一提。使用sort 并依赖元组的默认字典顺序太容易了,想着“嘿,如果它对元组进行排序,那么它会按元组的第一个元素排序”,而不是打扰sortBy 和按第一个元素排序的显式规范;但是在这个过程中不小心失去了排序的稳定性。
  • 通知太多,没有点赞 :D 但是,我会提到排序问题。
【解决方案2】:

这个怎么样? (注意Foldable t 可以是一个列表)

% ghci
λ> :m +Data.Map
λ> data P = P1 | P2 | P3 deriving (Eq, Ord, Show)
λ> let input = [(8, P1), (8, P2), (10, P3)] :: [(Int, P)]
λ> :type Prelude.foldl
_ :: Foldable t => (b -> a -> b) -> b -> t a -> b
λ> :type Prelude.foldl (\mp (k, p) -> insertWith (<>) k [p] mp)
_ :: (Foldable t, Ord k) => Map k [a] -> t (k, a) -> Map k [a]
λ> :type Prelude.foldl (\mp (k, p) -> insertWith (<>) k [p] mp) mempty
_ :: (Foldable t, Ord k) => t (k, a) -> Map k [a]
λ> :type Prelude.foldl (\mp (k, p) -> insertWith (<>) k [p] mp) mempty input
_ :: Map Int [P]
λ> Prelude.foldl (\mp (k, p) -> insertWith (<>) k [p] mp) mempty input
fromList [(8,[P2,P1]),(10,[P3])]
λ> toList (Prelude.foldl (\mp (k, p) -> insertWith (<>) k [p] mp) mempty input)
[(8,[P2,P1]),(10,[P3])]

【讨论】:

  • foldl+insertWith 的拼写可能更好fromListWith。这是我在链接问题“如何使用 Haskell 对列表中的类似项目进行分组?”中建议的答案。我认为该问题不是重复的(因此这不是答案),因为这里提出的问题是“我做错了什么?”而不是“我应该怎么做?”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多