Algorithm 理解Haskell中的矩阵转置函数

Algorithm 理解Haskell中的矩阵转置函数,algorithm,list,haskell,Algorithm,List,Haskell,这个矩阵换位函数是有效的,但我试图理解它的一步一步执行,但我不明白 transpose:: [[a]]->[[a]] transpose ([]:_) = [] transpose x = (map head x) : transpose (map tail x) 与 它返回: [[1,4,7],[2,5,8],[3,6,9]] 我不明白连接操作符是如何处理map的。它是在同一个函数调用中连接x的每个头部?怎么做 这是吗 (map head x) 创建每个列

这个矩阵换位函数是有效的,但我试图理解它的一步一步执行,但我不明白

    transpose:: [[a]]->[[a]]
    transpose ([]:_) = []
    transpose x = (map head x) : transpose (map tail x)

它返回:

 [[1,4,7],[2,5,8],[3,6,9]]
我不明白连接操作符是如何处理map的。它是在同一个函数调用中连接x的每个头部?怎么做

这是吗

(map head x)

创建每个列表的头元素列表?

让我们看看函数对示例输入的作用:

transpose [[1,2,3],[4,5,6],[7,8,9]]
<=>
(map head [[1,2,3],[4,5,6],[7,8,9]]) : (transpose (map tail [[1,2,3],[4,5,6],[7,8,9]]))
<=>
[1,4,7] : (transpose [[2,3],[5,6],[8,9]])
<=>
[1,4,7] : (map head [[2,3],[5,6],[8,9]]) : (transpose (map tail [[2,3],[5,6],[8,9]]))
<=>
[1,4,7] : [2,5,8] : (transpose [[3],[6],[9]])
<=>
[1,4,7] : [2,5,8] : (map head [[3],[6],[9]]) : (transpose (map tail [[3],[6],[9]]))
<=>
[1,4,7] : [2,5,8] : [3, 6, 9] : (transpose [[], [], []])
<=>
[1,4,7] : [2,5,8] : [3, 6, 9] : [] -- because transpose ([]:_) = []
<=>
[[1,4,7],[2,5,8],[3,6,9]]
创建每个列表的头元素列表


是的,它是。

cons操作符
a
类型的对象附加到
[a]
类型的列表中。在

(map head x) : transpose (map tail x)
LHS是一个列表(
a=[b]
),而RHS是一个列表列表(
[a]=[b]]
),因此这种构造是有效的。结果是

[x,y,z,...] : [[a,b,...],[d,e,...],...] = [[x,y,z,...], [a,b,...],[d,e,...],...]

在您的例子中,
map head x
map tail x
分割矩阵

x = [[1,2,3],[4,5,6],[7,8,9]]
进入

(是的,
map head x
是每个列表的head元素的列表。)第二部分被转置(有关详细步骤,请参见的答案)以形成

因此,将
[1,4,7]
与此进行比较

map head x : transpose (map tail x) =  [1,4,7] : [[2,5,8],[3,6,9]]
                                    = [[1,4,7] ,  [2,5,8],[3,6,9]]

ghci
是您的朋友:

*Main> :t map head map head :: [[a]] -> [a] *Main> :t map tail map tail :: [[a]] -> [[a]] 但这无法进行打字检查:

<interactive>:1:14: No instance for (Num [a]) arising from the literal `3' at :1:14 Possible fix: add an instance declaration for (Num [a]) In the expression: 3 In the second argument of `map', namely `[1, 2, 3]' In the expression: map head [1, 2, 3] 变得有点复杂了

*Main> map head [[1,2,3],[4,5,6]]
[1,4]
做同样的事,但不是给予


如您所见,
转置
的定义是用
映射头x
重复地切掉第一行,然后转置其余的行,即
映射尾x

顺便说一句,当给定像
[[1,2,3],[1,2]]这样的输入时,此函数不起作用。但是,
Data.List
中的转置函数将接受此输入,并返回
[[1,1],[2,2],[3]


当我们调用递归的
转置
代码时,我们需要去掉
[]

这些东西是一样的:

map head xxs
map (\xs -> head xs) xxs
这是lambda表达式,这意味着我为每个x返回x的头 例如:


这很简单

如果要使用
head
tail
转换矩形阵列,请确保事先列数相同,然后可以执行以下操作:

rectangularTranspose :: [[a]] -> [[a]]
rectangularTranspose m = rectTrans m []
  where
    rectTrans [] a = a
    rectTrans ([]:xss) a = rectTrans xss a
    rectTrans ((x:xs):xss) a = rectTrans a ((x:map head xss): rectTrans (xs:map tail xss) a)


显然,它也适用于方形数组和单例,但当标准实现为您提供更多选项时,我看不出它有多大用处。

当时您使用的是调试器吗?也许是他自己编写的?写起来不太复杂。()不是在创建列表
map
在()和
transpose
中都创建列表。@DminReader:
()
是单位,它不会出现在代码中的任何地方<代码>[]
是空列表。这不是答案,但一般来说,当我试图在Haskell中对某些内容进行思考时,我会花一些时间在GHCi中处理它。在一些列表中尝试“映射头”或“映射尾”,您将亲眼看到它们是如何工作的。如果你来自一个迫切需要的世界,地图和褶皱可能会有点难以探索。它们是你的主要循环结构——基本上取代了“for”和“while”——因此你很快就会爱上它们;海报不仅仅是试图调用转置,而是想弄清楚它为什么会起作用。
*Main> map head [1,2,3]
<interactive>:1:14: No instance for (Num [a]) arising from the literal `3' at :1:14 Possible fix: add an instance declaration for (Num [a]) In the expression: 3 In the second argument of `map', namely `[1, 2, 3]' In the expression: map head [1, 2, 3]
*Main> map head [[1,2,3]]
[1]
*Main> map head [[1,2,3],[4,5,6]]
[1,4]
*Main> map tail [[1,2,3],[4,5,6]]
[[2,3],[5,6]]
map head xxs
map (\xs -> head xs) xxs
   map head [[1,2,3],[4,5,6],[7,8,9]]
-> map (\xs -> head xs) [[1,2,3],[4,5,6],[7,8,9]]
-> [head [1,2,3], head [4,5,6], head [7,8,9]]
-> [1,4,7]
rectangularTranspose :: [[a]] -> [[a]]
rectangularTranspose m = rectTrans m []
  where
    rectTrans [] a = a
    rectTrans ([]:xss) a = rectTrans xss a
    rectTrans ((x:xs):xss) a = rectTrans a ((x:map head xss): rectTrans (xs:map tail xss) a)