简体   繁体   English

交换Haskell中的列表列表

[英]Interleave List of Lists in Haskell

I was wondering how could I write a function in Haskell that interleaves a list of lists into a single lists, for example, if I had a function called 我想知道如何在Haskell中编写一个将列表列表交织到单个列表中的函数,例如,如果我有一个函数调用

interleavelists :: [[a]] -> [a]

it should be able to interleave the elements. 它应该能够交错元素。

Example: [[1,2,3] [4,5,6] [7,8]] --> [1,4,7,2,5,8,3,6] . 例如: [[1,2,3] [4,5,6] [7,8]] --> [1,4,7,2,5,8,3,6]

The lists can be both finite or infinite... Can I use foldr ? 列表可以是有限的也可以是无限的...我可以使用foldr吗?

The quickest way to write it is to use transpose from Data.List . 编写它的最快方法是使用Data.List transpose

import Data.List

interleavelists :: [[a]] -> [a]
interleavelists = concat . transpose

transpose picks the first element of each non-empty list of its argument, putting them into one list, and after that, transpose s the list of tail s of the argument's elements. transpose选择其参数的每个非空列表的第一个元素,将它们放入一个列表中,然后transpose参数元素的tail列表。 concat enating the lists of transpose 's result interleaves the lists as desired. concat enating的列表transpose如所期望的结果交织列表。 It works if some element lists are infinite, but if the list of lists itself has infinitely many elements, it of course never gets past the list of head s. 如果某些元素列表是无限的,它可以工作,但如果列表列表本身具有无限多个元素,那么它当然永远不会超过head列表。 But handling that case is problematic anyway. 但无论如何处理这种情况是有问题的。

Using foldr to interleave the lists is not trivial. 使用foldr交错列表并非易事。 Suppose you had 假设你有

interleavelists xss = foldr something zero xss

interleavelists [] should probably produce [] , so that'd be the zero value. interleavelists []应该产生[] ,所以'是zero值。 And

interleavelists [xs] = xs

seems natural, so 看起来很自然,所以

something xs [] = xs

But what if the second argument isn't [] ? 但如果第二个参数不是[]怎么办? Then you want to insert the elements of the first argument of something at varying distances into the second argument. 然后,你要插入的第一个参数的元素something在不同的距离到第二个参数。 But at which distances? 但在哪个距离? If all lists have the same length, the distances for each list are constant, then you could pass the distance as a further parameter, 如果所有列表具有相同的长度,则每个列表的距离都是常量,那么您可以将距离作为另一个参数传递,

interleavelists = snd . foldr insertAtDistance (0, [])
  where
    insertAtDistance xs (d, ys) = (d+1, helper d xs ys)
    helper _ [] ws = ws
    helper k (b:bs) cs = b : us ++ helper k bs vs
      where
        (us,vs) = splitAt k cs

That isn't very pretty, and if the lists are not all the same length will produce what probably isn't the desired output. 这不是很漂亮,如果列表不是全部相同的长度将产生可能不是所需的输出。 But if the lists all have the same length, it does the job. 但是如果列表都具有相同的长度,那么它就完成了工作。

Simple recursive version: 简单的递归版本:

inter :: [[a]] -> [a]
inter [] = []
inter xs = inter2 (filter (\x -> not (null x)) xs)
   where inter2 xs = map head xs ++ inter (map tail xs)

Now, about foldr... 现在,关于折叠......

The "standard" (or perhaps, famous) way of interleaving lists, back in the jolly days of SICP (and later, Reasoned Scheme), was 在SICP(以及后来的推理方案)的欢乐时代,“标准”(或许是着名的)交错列表的方式是

infixr 5 ++/

[]     ++/ ys = ys
(x:xs) ++/ ys = x:(ys ++/ xs)

It can be used with foldr , 它可以与foldr一起使用,

*Main> foldr (++/) [] [[1,2,3],[4,5,6],[7,8]]
[1,4,2,7,3,5,8,6]

This obviously does not produce the result in the order you wanted, but it will OTOH work OK when the input list of lists is infinite. 这显然不会按照你想要的顺序产生结果,但是当列表的输入列表是无限的时候OTOH工作正常。 I don't think there is a way to satisfy both requirements at the same time, as we have no way of knowing whether an input list will be infinite or not. 我认为没有办法同时满足这两个要求,因为我们无法知道输入列表是否是无限的。

The above results are what the function insertAtDistance from Daniel's answer would produce, if modified to always insert at a distance of 1 , instead of d+1 : 上面的结果是Daniel的答案中函数insertAtDistance会产生的,如果修改为总是插入距离1而不是d+1

insertAtDistance xs (d, ys) = (1, helper d xs ys)

When defined with d+1 it produces "flat" interleaving, whereas foldr (++/) [] produces skewed interleaving: 当用d+1定义时,它产生“平坦”交错,而foldr (++/) []产生偏斜交错:

*Main> take 20 $ foldr (++/) [] [cycle[i] | i<-[1..]]
[1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,5,1,2,1,3]

we could do what you want 我们可以做你想做的事

testList = [[1,2,3],[4,5,6],[7,8]]

interleave l = foldr' (concat [map (\x -> f x idx) l | idx <- [0..]])  
    where
        f x n = if (length(x)<=n) then Nothing else Just (x !! n)
        foldr' (x:xs) = case x of 
                         Nothing -> []
                         Just a  -> (:) a (foldr' xs)   

As required interleave [[1,2,3] [4,5,6] [7,8]] => [1, 4, 7, 2, 5, 8, 3, 6] 交错[[1,2,3] [4,5,6] [7,8]] => [1,4,7,2,5,8,3,6]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM