首页 文章

不同长度的邮编列表

提问于
浏览
1

我刚刚开始使用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 回答

  • 2

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

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

    import Data.List.Split (chunksOf)
    
    splitList as bs = zs : if null xs then map ( zip  as) (chunksOf n ys)
                                      else map (`zip` bs) (chunksOf n xs)
       where
       (zs, n, xs, ys) = (zip as bs, length zs, drop n as, drop n bs)
    

    适用于这两种情况,也有无限列表 .

  • 1

    你有正确的方法,但你只需要递归,而不是枚举你经历的周期数:

    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)]]
    
  • 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 this solution only work for finite lists

  • 0

    另一个答案显示了如何重用库函数 . 但是,它有一个小缺陷:它在开始返回结果之前将其参数强制进入内存,因此它与Haskell的其余惰性基础结构不能完全一致 . 在这个答案中,我将展示如何编写一个版本:

    • 开始立即产生输出(一旦明确两个列表都是非空的)

    • 使用无限输入

    • 在任何给定时间强制最多两倍长度的元素值的元素进入内存

    我们将构建在另一个答案中使用的所有部分,但是它们的lazified版本:不是使用 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 的lazified版本实现它 . 后者应该使用"lazy"长度将其列表参数拆分为 . 我们小心翼翼地尽可能快地开始生成元素 - 也就是说,只要我们知道要分割的索引大于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 作为子例程 . 我们再次将"lazy"数作为我们的第一个参数;并且我们尽可能早地生成输出列表的结构,将 (:) 调用为最外层的函数调用 .

    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)]]
    

相关问题