将具有相同长度的列表元组转换为任意的元组列表 - Haskell

时间:2016-10-31 12:19:20

标签: haskell tuples

我试图转换相同长度的列表元组: ([Int], [Int], ..)到元组列表[(Int, Int, ...)]。这可以通过以下代码实现预定的尺寸:

buildList :: ([a], [a], [a], [a], [a], [a], [a], [a]) -> [(a, a, a, a, a, a, a, a)]           
buildList ([], [], [], [], [], [], [], []) = []                                               
buildList (a:as, b:bs, c:cs, d:ds, e:es, f:fs, g:gs, h:hs) = (a, b, c, d, e, f, g, h) : buildList (as, bs, cs, ds, es, fs, gs, hs)

正如你可能看到的那样,当我需要列表中的大量项目时,这并不是很好,如果它适用于任意值,它会更加清晰。

所以我的问题是,你有一个函数为任意长度的元组预先形成这个操作吗?

1 个答案:

答案 0 :(得分:3)

正如这类问题一样,“你应该”和“你能”的答案是不同的。在这里,我将严格回答第二个问题。

解决这个问题的第一部分是n元元组或(听起来特别奇特)产品的表示。让我们这样做(不使用任何导入,这样整个东西仍然是自包含的,而且因为在我目前使用的机器上,Stack是行为不端):

{-# language DataKinds, KindSignatures, TypeOperators, GADTs #-}
{-# language FlexibleInstances, FlexibleContexts #-}
{-# language TypeFamilies #-} -- This will be needed later

data Product (ts :: [*]) where
    Nil :: Product '[]
    Cons :: t -> Product ts -> Product (t ': ts)

instance Show (Product '[]) where
    show Nil = "()"

instance (Show t, Show (Product ts)) => Show (Product (t ': ts)) where
    show (Cons x xs) = let '(':s = show xs
                       in concat ["(", show x, ",", s]

所以这给了我们写一些类似

的方法
*Main> myPair = Cons 1 $ Cons "Foo" $ Cons True Nil 
*Main> :t myPair
myPair :: Num t => Product '[t, [Char], Bool]
*Main> myLists = Cons [1, 2] $ Cons ["Foo", "Bar", "Baz"] Nil
*Main> :t myLists
myLists :: Num t => Product '[[t], [[Char]]]

使用这种类型,至少我们可以开始考虑我们的n-ary压缩函数zipN的类型应该是什么:

zipN :: Product '[[a], [b], ...] -> [Product '[a, b, ...]]

但是,我们仍然需要一种方法以某种方式将列表元组Product '[[a], [b], ...]转换为元素元组Product '[a, b, ...]。我发现最简单的是使用关联的类型系列来进行转换和锁步的实际压缩:

class IsLists (ts :: [*]) where
    type Unlists ts :: [*]
    zipN :: Product ts -> [Product (Unlists ts)]

instance IsLists '[] where
    type Unlists '[] = '[]
    zipN Nil = []

instance (IsLists ts) => IsLists ([t] ': ts) where
    type Unlists ([t] ': ts) = t ': Unlists ts

    -- Handling the tail is special to ensure we don't end up zipping everything
    -- with the empty list coming from zipN []
    zipN (Cons xs yss) = case yss of
        Nil -> map (`Cons` Nil) xs
        _ -> zipWith Cons xs (zipN yss)

示例:

*Main> zipN myLists
[(1,"Foo",),(2,"Bar",)]
*Main> :t it
it :: Num t => [Product '[t, [Char]]]

请注意,这与常规zip的行为类似,因为结果列表的长度由元组中的最短列表指定。