【问题标题】:Haskell: create a tuple of lists from an input listHaskell:从输入列表创建列表元组
【发布时间】:2020-06-26 09:14:56
【问题描述】:

我正在尝试设置一些功能来帮助我正在进行的当前项目。我是 Haskell 的新手,正在努力实现我想要的功能。

我有一个列表[a],并希望它输出四个不同列表([b],[b],[b],[b]) 的元组,其中列表[a] 中的每个项目依次放入输出元组中的下一个列表。所以输入列表[a]中的第一个元素进入第一个列表[b][a]中的第二个元素进入第二个列表[b][a]中的第三个元素进入第三个列表@987654334 @, 等等。 我曾尝试使用 chunksOf 和 splitEvery/splitAt 但无法获得正确的输出。帮助将不胜感激!谢谢!

【问题讨论】:

  • 我认为输出是 [a] -> ([a], [a], [a], [a])?所以使用[a]s 作为输出,而不是[b]s。你能分享你的实现吗?
  • 是的,我正计划更改 [a] 的每个元素的数据类型,因为它被放置在 [b] 中的相应插槽中,这就是我在这种情况下更改名称的原因。但是,对于这个基本实现,我认为说 [a] 将输出是正确的,因为我仍然需要将所需的数据类型映射到 [a] 中的每个元素,因为它被传递给输出元组
  • 这里问了一个类似的问题:Haskell: Splitting list into tuple of two new lists 这个问题是关于二元组(我猜是二元组;)),但是一些答案(dave4220 和 Yitz)可以很容易地扩展到您的情况。
  • @MikaelF 感谢您的链接。 this answer 以及 foldr (\a ~(x,y) -> (a:y,x)) ([],[]) 代码 sn-p(这也是 here 并且可能更早知道)。将其扩展到 3 元组或 n 元组...交换是旋转的,唯一需要决定的是,朝哪个方向? :)
  • (我发现我的 this old answer 正在处理这个问题,尽管在 Scheme 中。)还有 this question

标签: list haskell split tuples


【解决方案1】:

您每次“旋转”四元组并添加到第一个元素。所以我们可以使用foldr 模式来实现它,如下所示:

toFour :: [a] -> ([a], [a], [a], [a])
toFour = foldr (\a (bs, cs, ds, as) -> (a:as, bs, cs, ds)) ([], [], [], [])

或具有无可辩驳的模式:

toFour :: [a] -> ([a], [a], [a], [a])
toFour = foldr (\a ~(bs, cs, ds, as) -> (a:as, bs, cs, ds)) ([], [], [], [])

所以这里(bs, cs, ds, as)是我们为列表尾部生成的4元组,我们向右“旋转”以构造元组(as, bs, cs, ds),然后将项目a添加到第一个列表的 4 元组。

对于整数列表,这给了我们:

Prelude> toFour [1..2]
([1],[2],[],[])
Prelude> toFour [1..3]
([1],[2],[3],[])
Prelude> toFour [1..4]
([1],[2],[3],[4])
Prelude> toFour [1..5]
([1,5],[2],[3],[4])
Prelude> toFour [1..6]
([1,5],[2,6],[3],[4])
Prelude> toFour [1..10]
([1,5,9],[2,6,10],[3,7],[4,8])

当我们使用无可辩驳的模式时,这是懒惰的,因此我们可以例如分配无限列表的元素,然后以获取第二项的前 10 个元素为例:

Prelude> (\(_, l, _, _) -> take 10 l) (toFour [1..])
[2,6,10,14,18,22,26,30,34,38]

【讨论】:

  • 这是一个聪明的解决方案!我认为它也可以通过使用列表或序列而不是元组来推广到任意数量的列表。
  • 我认为您可以使用标准的基于两个列表的队列。 haskell bucket i = uncurry (++) . fmap reverse . foldr f ([], replicate i []); where f x (xs,y:ys) = ((x:y):xs,ys); f x (xs,[]) = let y:ys = reverse xs in ([x:y],ys);
  • @ReinHenrichs 这远不是这个技巧第一次出现在 SO 答案中。我自己在一两个答案中使用了它(或者是在 cmets 中?)。由于我自己没有想出它,我总是指出我第一次看到它是在用户“Ed'ka”的 F#(或者它是 ML?)的回答中——我记得被它吓坏了。他的代码是用于列表的二元组,在每一步交换元组;想出三元组泛化是一个很好的练习,我记得,:) 找出正确的元组旋转方向(当然,它可以用于任何 n)。
  • @ReinHenrichs this。 (结果我什至给了这个答案一个赏金:))
  • 原来它在 haskellwiki since forever 中。 :)
【解决方案2】:

定义

f n = transpose . chunksOf n
g xs = let [xs1, xs2, xs3, xs4] = f 4 xs in (xs1, xs2, xs3, xs4) 

f 是通用解决方案。这给了

Prelude> g ['a' .. 'z']
("aeimquy","bfjnrvz","cgkosw","dhlptx") 

签名

Prelude> :t g
g :: [a] -> ([a], [a], [a], [a])

Prelude> :t f
f :: Int -> [a] -> [[a]]

信息:

【讨论】:

  • map (take 5) . transpose . chunksOf 3 $ [1..] 由于某种原因出现分歧。 :) 必须在 transpose 的顶部加上 take 3 以保持适当的懒惰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多