【问题标题】:transpose a 2d list and convert it to a 1d list转置二维列表并将其转换为一维列表
【发布时间】:2021-12-18 22:38:30
【问题描述】:

我有一个二维列表 l,并且

[l!!y!!x| x<-[0..length l-1], y<-[0..length l-1]]

将生成一个交换行和列的一维列表。

如何在没有列表理解的情况下实现这一点(即使用地图)?

【问题讨论】:

  • 也不使用递归
  • 请将此要求添加到您的原始问题中,因为它会破坏人们给出的答案。

标签: list haskell list-comprehension


【解决方案1】:

将问题分解为多个部分。要转置单行,您需要返回每行包含单个元素的列。一个 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 -&gt; [x] 也可以写成(: [])pure 的运算符部分Applicative

当您在外部列表(输入行)上进行模式匹配时,您会将矩阵的顶行与其余行分开。矩阵的转置是在剩余行的转置之前的第一行的转置。您可以使用(++)(可以写成Semigroup 中的(&lt;&gt;))垂直连接两个矩阵(即,将一个放在另一个矩阵之上)或使用zipWith (++) 水平连接(分别为zipWith (&lt;&gt;))(即,将一个放在旁边另一个)。转置矩阵水平连接:

{-   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) =&gt; t (f b) -&gt; f (t b) 是一种广义转置的观察。

【讨论】:

    【解决方案2】:

    既然你想要它没有显式递归,

    zipWith 也是二进制的map(在其他一些语言中,map 实际上可以接受任意数量的参数列表)。因此,

    transposed = foldr (zipWith (:)) (repeat [])
    

    尝试一下:

    > transposed [[1,2,3],[11,12,13],[21,22,23,24]]
    [[1,11,21],[2,12,22],[3,13,23]]
    

    这可以简单地与concat :: [[a]] -&gt; [a] 组合以连接转置的列表。

    【讨论】:

      【解决方案3】:

      我们可以使用 recursion 为此:每次我们产生列表的heads,然后在尾部递归,所以:

      catTranspose :: [[a]] -> [a]
      catTranspose ([]:_) = []
      catTranspose xs = map head xs ++ …

      我在哪里填写 作为练习。它应该进行递归调用,我们将xs 中的每个项目映射到它的尾部。

      如果列表是矩形的,这将起作用,对于非矩形 2d 列表,您需要使用更安全的函数,因此不会像 head 在空列表上那样出错。

      也可以放弃显式递归,例如使用 unfoldr :: (b -&gt; Maybe (a, b)) -&gt; b -&gt; [a]concat :: Foldable f =&gt; f [a] -&gt; [a] 执行递归。

      【讨论】:

      • 不会([]:_) 错过[[1,2,3],[],[4,5,6]] 中的空列表后面的任何内容吗?
      • @user1984:正如指定的那样,它确实只适用于矩形列表,否则它将在该空列表上调用headtail。可以通过使用concatMap (take 1) xs ++ ... 来解决这个问题,并使用safeTail :: [a] -&gt; [a] 来处理空列表返回一个空列表。我把它作为一个额外的练习(见最后一段)。
      • 感谢您的解释:D
      猜你喜欢
      • 2022-12-29
      • 2022-07-30
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 2019-09-17
      • 2015-11-20
      相关资源
      最近更新 更多