将问题分解为多个部分。要转置单行,您需要返回每行包含单个元素的列。一个 1 x n 矩阵变成一个 n x 1 矩阵:
{- 1 2 ... n 1
2
...
n
-}
transpose [row] = map (\ x -> [x]) row
例如:transpose [[1, 2, 3]] = [[1], [2], [3]]。
\ x -> [x] 也可以写成(: []) 或pure 的运算符部分Applicative。
当您在外部列表(输入行)上进行模式匹配时,您会将矩阵的顶行与其余行分开。矩阵的转置是在剩余行的转置之前的第一行的转置。您可以使用(++)(可以写成Semigroup 中的(<>))垂直连接两个矩阵(即,将一个放在另一个矩阵之上)或使用zipWith (++) 水平连接(分别为zipWith (<>))(即,将一个放在旁边另一个)。转置矩阵水平连接:
{- 1 2 ... n 1 a ... p
a b ... c 2 b ... q
....... .........
p q ... r n c ... r
-}
transpose (top : down)
= zipWith (++) (transpose [top]) (transpose down)
例如:transpose [<strong>[1, 2]</strong>, [3, 4], [5, 6]] = [<strong>[1]</strong> ++ [3, 5], <strong>[2]</strong> ++ [4, 6]]。
这也可以表示为zipWith (:) top (transpose down),因为我们知道我们需要添加单个元素;这跳过了一些多余的包装工作,然后立即展开输入行/输出列的元素。
最后,no行的空矩阵的转置也是空矩阵。
transpose [] = []
把这些放在一起:
transpose :: [[a]] -> [[a]]
transpose [r] = map (: []) r
transpose (r : rs) = zipWith (:) r (transpose rs)
transpose [] = []
跟进:考虑此代码如何响应输入为非矩形或具有无限数量的行或列的边缘情况。
正如 Will Ness 的回答所表明的那样,这个递归函数几乎等同于右折叠,其中组合函数是 zipWith (:) 列的前置,而基本累加器是不定高度的空列 repeat [] ,即transpose m = foldr (zipWith (:)) (repeat []) m,或eta-reduced,transpose = foldr (zipWith (:)) (repeat [])。
但是,它们在边缘情况下有所不同:当给定一个空输入时,折叠会产生一个无限列表。该版本也等同于getZipList . traverse ZipList,基于traverse id :: (Traversable t, Applicative f) => t (f b) -> f (t b) 是一种广义转置的观察。