转置列表列表

注意到 zip 将列表元组转换为元组列表,

ghci> uncurry zip ([1,2],[3,4])
[(1,3), (2,4)]

transposesequenceA 的类型之间的相似性,

-- transpose exchanges the inner list with the outer list
--           +---+-->--+-+
--           |   |     | |
transpose :: [[a]] -> [[a]]
--            | |     |   |
--            +-+-->--+---+

-- sequenceA exchanges the inner Applicative with the outer Traversable
--                                             +------>------+
--                                             |             |
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
--                                                |       |
--                                                +--->---+

我们的想法是使用 []TraversableApplicative 结构将 sequenceA 作为一种 n-ary zip 进行部署,将所有内部列表逐点拼凑在一起。

[] 的默认优先选择Applicative 实例不适合我们使用 - 我们需要一个 zippy``Applicative。为此,我们使用了 Control.Applicative 中的 ZipList newtype。

newtype ZipList a = ZipList { getZipList :: [a] }

instance Applicative ZipList where
    pure x = ZipList (repeat x)
    ZipList fs <*> ZipList xs = ZipList (zipWith ($) fs xs)

现在我们通过遍历 ZipList Applicative 免费获得 transpose

transpose :: [[a]] -> [[a]]
transpose = getZipList . traverse ZipList

ghci> let myMatrix = [[1,2,3],[4,5,6],[7,8,9]]
ghci> transpose myMatrix
[[1,4,7],[2,5,8],[3,6,9]]