简体   繁体   English

为什么Data.List中Haskell的`transpose` function不使用`head`和`tail`?

[英]Why does Haskell's `transpose` function in Data.List not use `head` and `tail`?

I've just spent some time working on a problem for which I needed to translate a list of lists a certain way.我刚刚花了一些时间来解决我需要以某种方式翻译列表列表的问题。 After successfully working through it I came up with this solution:成功完成后,我想出了这个解决方案:

translate :: [[a]] -> [[a]]
translate ([]:xss) = []
translate xss      = (map head xss) : (translate $ map tail xss)

Very shortly afterwards, i realized that I was simply trying to transpose a matrix ... I thought "I probably lost a lot of time trying to do this, as surely Haskell has a function in its standard libraries to do such a common operation."不久之后,我意识到我只是想转置一个矩阵......我想“我可能会浪费很多时间来尝试这样做,因为 Haskell 在其标准库中肯定有一个 function 来执行这种常见的操作。 " So I decided to check, and unsurprisingly I found that the Data.List module includes a transpose function.所以我决定检查一下,不出所料,我发现Data.List模块包含一个transpose function。

But what was actually surprising to me was the way it was defined:但令我惊讶的是它的定义方式:

transpose               :: [[a]] -> [[a]]
transpose []             = []
transpose ([]   : xss)   = transpose xss
transpose ((x:xs) : xss) = (x : [h | (h:_) <- xss]) : transpose (xs : [ t | (_:t) <- xss])

It uses list comprehensions instead of head and tail , which I thought was interesting but could not figure out why it did so.它使用列表推导而不是headtail ,我认为这很有趣,但不知道为什么这样做。

Is there a reason why it is better to use list comprehension instead of the pre-defined head and tail functions (with map ) , the way that I did with my function?是否有理由像我对map那样使用列表理解而不是预定义head函数(使用tail更好? Is it something to do with the efficiency of the code?是否与代码的效率有关?

I am not necessarily new to Haskell, but I am not an expert either.我不一定是 Haskell 的新手,但我也不是专家。 That being said, more technical answers and explanations would also be greatly appreciated as I am hoping to learn more about the intricacies of the language going forward (and even if I don't understand the reason now I will know what I have to research).话虽如此,更多技术性的答案和解释也将不胜感激,因为我希望更多地了解未来语言的复杂性(即使我现在不明白原因,我也会知道我必须研究什么) .

For empty lists, functions like head and tail will error.对于列表, headtail等函数会出错。 Note that if you write:请注意,如果您编写:

[h | (h:_) <- xss]

then this is not equivalent to map head xss .那么这等同于map head xss Indeed, the above list comprehension is equivalent to:事实上,上面的列表推导等价于:

let ok (h:_) = pure h
    ok _ = fail "…"
in xss >>= ok

So in case the pattern matching fails, then we return a fail "" value.所以如果模式匹配失败,那么我们返回一个fail ""值。 For a list this is the empty list:对于列表,这是空列表:

Prelude> fail "" :: [Int]
[]

This is important for non-rectangular lists that we want to transpose, for example:这对于我们想要转置的非矩形列表很重要,例如:

Prelude Data.List> transpose [[1,4,2,5],[1,3], [1,9,5,8]]
[[1,1,1],[4,3,9],[2,5],[5,8]]

Si it will transform: Si 它将转变:

[ 1 4 2 5]
[ 1 3 ]
[ 1 9 5 8]

to:至:

[1 1 1]
[4 3 9]
[2 5]
[5 8]

Whereas if one uses head and tail eventually when it aims to calculate the head and tail in the third row, it will crash on the [1,3] list:然而,如果一个head最终在计算第三行的tail时使用了headtail[1,3]列表中崩溃:

Prelude Data.List> transpose' [[1,4,2,5],[1,3], [1,9,5,8]]
[[1,1,1],[4,3,9],[2,*** Exception: Prelude.head: empty list

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM