不同长度的邮编列表

时间:2018-12-09 14:45:56

标签: haskell

我只是从Haskell开始,我不确定如何执行此操作,但是当我输入[1,2,3,4] [5,6,7,8,9,10,11 ,12]输出应为[[(1,5),(2,6),(3,7),(4,8)] [(1,9),(2,10),(3,11 ),(4,12)]]。我试图做某事,请参阅下面的代码。但这仅适用于特定大小的列表。我确信必须有一种方法可以更有效地执行此操作,并且对于任何大小的递归列表,有人可以帮助我吗?

splitlist :: [a] -> [b] -> [[(a,b)]] 
    splitlist list1 list2 = [a,b,c] where
        n = length list1
        a = zip list1 list2
        nlist = drop n list2
        b = zip list1 nlist 
        nnlist = drop n nlist 
        c = zip list1 nnlist

4 个答案:

答案 0 :(得分:2)

第一个列表中两个列表中的最长列表时,first answer不起作用; second answer对于这样一个简单的任务来说似乎太长了。

修改第一个答案的代码很容易,

$tid

在两种情况下都可以使用,也可以使用无限列表。

答案 1 :(得分:1)

一个有趣的问题! cycle在这种情况下可能会有所帮助,它会重复列出无限时间的元素,例如

cycle [1,2,3,4] = [1,2,3,4,1,2,3,4,1,....]

因此,何时使用[5,6,7,8,9,10,11,12]进行压缩将是理想的结果[(1,5),(2,6),(3,7),(4,8),(1,9),(2,10),(3,11),(4,12)],然后使用chunksOf(在Data.List.Split中定义)将其分组为[[(a, b)]]类型为:

splitList::[a]->[b]->[[(a,b)]]
splitList [] _ = []
splitList _ [] = []    
splitList list1 list2 
    | length list1 <= length list2 = doSplit (length list1) (cycle list1) list2
    | otherwise                    = doSplit (length list2) list1 (cycle list2)
    where doSplit len xs ys        = chunksOf len $ zip xs ys

P.S此解决方案仅适用于有限列表

答案 2 :(得分:1)

另一个答案显示了如何重用库函数。但是,它有一个小缺陷:它会在开始返回结果之前将其参数强制存储到内存中,因此它无法与Haskell其余的惰性基础结构完美地配合使用。在这个答案中,我将展示如何编写一个版本:

  1. 立即开始产生输出(只要这两个列表都是非空的)
  2. 使用无限输入进行工作
  3. 在任何给定时间最多将较短列表元素的长度的两倍强加到内存中

我们将构建其他答案中使用的所有片段,但将其简化版本:我们将隐式地使用类型为Int的列表,而不是使用[a]作为长度代表自己的长度。我们需要的第一件事是比较两个惰性数字。稍后会发现,让我们的比较返回不仅是哪个数字更大,而且两个数字的差也很方便。

data LazyOrdering a b
    = LazyLT [b]
    | LazyEQ
    | LazyGT [a]
    deriving (Eq, Ord, Read, Show)

lazyCompare :: [a] -> [b] -> LazyOrdering a b
lazyCompare [] [] = LazyEQ
lazyCompare [] bs = LazyLT bs
lazyCompare as [] = LazyGT as
lazyCompare (ah:at) (bh:bt) = lazyCompare at bt

接下来,我们需要chunksOf;我们将以splitAt的简化版本来实现它。后者应采用“惰性”长度来分割其列表参数。我们非常小心地尽快开始生成元素-也就是说,一旦我们知道要拆分的索引大于0并且要拆分的列表是非空的,就可以了。

lazySplitAt :: [a] -> [b] -> ([b], [b])
lazySplitAt [] bs = ([], bs)
lazySplitAt _ [] = ([], [])
lazySplitAt (_:as) (b:bs) = (b:bb, be) where ~(bb, be) = lazySplitAt as bs

我们的惰性chunksOf变体现在可以将lazySplitAt用作子例程。同样,我们将“惰性”数字作为第一个参数。并且我们会尽早生成输出列表的结构,将(:)称为最外层函数调用。

lazyChunksOf :: [a] -> [b] -> [[b]]
lazyChunksOf as bs = bb : case be of
    [] -> []
    _  -> lazyChunksOf as be
    where ~(bb, be) = lazySplitAt as bs

有了这些内容之后,我们可以使用基本上相同的实现,将我们的惰性变体替换为length / (<=) / chunksOf

zipCycle :: [a] -> [b] -> [[(a,b)]]
zipCycle [] _ = []
zipCycle _ [] = []
zipCycle as bs = zip as bs : case lazyCompare as bs of
    LazyLT bt -> lazyChunksOf as (zip (cycle as) bt)
    LazyEQ    -> []
    LazyGT at -> lazyChunksOf bs (zip at (cycle bs))

我们可以在ghci中试用:

> zipCycle "hi" "hello"
[[('h','h'),('i','e')],[('h','l'),('i','l')],[('h','o')]]

无限列表可以很好地用作任一参数:

> take 3 $ zipCycle "hi" [1..]
[[('h',1),('i',2)],[('h',3),('i',4)],[('h',5),('i',6)]]
> take 3 $ zipCycle [1..] "hi"
[[(1,'h'),(2,'i')],[(3,'h'),(4,'i')],[(5,'h'),(6,'i')]]

...或同时作为两个参数:

> take 1 . map (take 3) $ zipCycle [1..] [1..]
[[(1,1),(2,2),(3,3)]]

答案 3 :(得分:0)

您采用了正确的方法,但是只需要使其具有递归性即可,而不是枚举经过的周期数:

splitList :: [a] -> [b] -> [[(a,b)]]
splitList as [] = []
splitList as bs = zip as bs : splitList as (drop (length as) bs)

例如,

splitList [1,2] [1..10]
  == zip [1,2] [1..10] : splitList [1,2] [3..10]
  == zip [1,2] [1..10] : zip [1,2] [3..10] : splitList [1,2] [5..10]
  == ...
  == zip [1,2] [1..10] : zip [1,2] [3..10] : ... : zip [1,2] [9,10] : splitList [1,2] []
  == zip [1,2] [1..10] : zip [1,2] [3..10] : ... : zip [1,2] [9,10] : []
  ...
  == [[(1,1),(2,2)],[(1,3),(2,4)],...,[(1,9),(2,10)]]