有人可以向我解释这些Haskell功能吗?

时间:2009-05-25 20:48:45

标签: recursion functional-programming lazy-evaluation haskell

我过去曾经涉足过Haskell,最近又认真地回到了它,我正在阅读现实世界的haskell。他们已经发光的一些例子,我还没有理解。就在这一点:

myLength []     = 0
myLength (x:xs) = 1 + myLength (xs)

我不知道这是如何工作的,真正添加的是什么?递归如何返回可以添加的内容?我不明白。

我们在这里有一个:

splitLines [] = []
splitLines cs =
       let (pre, suf) = break isLineTerminator cs
       in  pre : case suf of 
                   ('\r':'\n':rest) -> splitLines rest
                   ('\r':rest)      -> splitLines rest
                   ('\n':rest)      -> splitLines rest
                   _                -> []

isLineTerminator c = c == '\r' || c == '\n'

这是如何工作的,什么是真正的附加?我没有看到case表达式的结果是如何预先连接到的。也许我只需要有人详细解释这些功能的评估。我必须遗漏一些非常重要的东西。

提前致谢!

编辑:我知道,这是一个复制粘贴失败。遗憾。

编辑2:看来我的困惑在于这些功能实际/返回/我现在已经解决了。谢谢你们的答案,终于点击了!我很感激!

5 个答案:

答案 0 :(得分:10)

至于第一个,它是一种非常基本的递归方式。但是,似乎缺少了一部分:

myLength [] = 0

它的工作原理是从列表中缩小一个元素并将一个元素添加到结果中。要想象,请考虑调用

myLength [1,2,3]

将评估为:

1 + myLength [2,3]
1 + 1 + myLength [3]
1 + 1 + 1 + myLength []
1 + 1 + 1 + 0

是3。

至于第二个,嗯,你已经把下一个换行符的字符串分成两个部分:pre和suf。现在,suf将以\ n,\ n或\ r或\ r \ n开头。我们想删除这些。所以我们使用模式匹配。看看rest变量本质上是suf变量减去初始换行符。

所以我们有pre,这是第一行,而rest,这是文本的其余部分。因此,为了继续将休息分成行,我们以递归方式调用splitLines并将结果连接到pre。

要想象,请说你有字符串“foo \ nbar \ r \ n \ nbaz”。

因此,在致电时,结果将是:

[ pre => foo, suf => \nbar\r\nbaz, rest => bar\r\n\baz ]
foo : splitLines bar\r\nbaz

然后再次调用splitLines,结果扩展为:

[ pre => bar, suf => \r\nbaz, rest = baz ]
foo : bar : splitLines baz
然后再一次:

[ pre => baz, suf => [], rest = [] ]
foo : bar : baz

这是最终结果。

答案 1 :(得分:4)

我认为myLength的定义错过了列表为空的情况:

myLength [] = 0
myLength (x:xs) = 1 + myLength (xs)

使用此定义,空列表的myLength为0. (x:xs)模式将列表解压缩到第一个项a中,以及包含其余项的列表,xs。如果列表中有一个项目,则xs是一个空列表,因此结果为1 + 0.依此类推。

当您首先查看基本案例时,最容易理解递归,然后查看每个递归级别如何构建在结果上。 (基本情况是函数不调用自身的情况。如果递归函数没有基本情况,则输出将是无限的。)

在第二个示例中,基本案例(案例陈述中的最后一个案例)也是一个空列表。所以pre将始终附加到列表中,这将产生一个新的,更长的列表。

答案 2 :(得分:2)

Re:myLength (x:xs) = 1 + myLength (xs) - 这是myLength定义的“一半”,它通过模式匹配说明,如果参数有一个头和一个tail,那么结果比尾部的递归尾调用更多 - 当参数不匹配x:xs时,需要另外一半说结果是0,即,当参数为空时列表。

在第二种情况下,通过case进行不同模式匹配的可能性更加明显。

BTW,懒惰不是一个关键问题 - ML,热切的评估,但模式匹配很像Haskell,将工作非常相似。看起来模式匹配是你真正需要了解的内容。

答案 3 :(得分:2)

首先,第一个例子应该是这样的(编辑:,看起来你现在修好了):

myLength []     = 0
myLength (x:xs) = 1 + myLength (xs)

它的工作原理如下:说我给它一个包含三个项目的列表,它返回一个加上尾部的长度(这是一个加上尾部的长度(这是一个加上尾部的长度,(此时为[],即1),即w),即3(最终答案)。也许嵌套的括号将帮助您理解它。 ; - )

答案 4 :(得分:1)

查看函数的类型签名是有益的。他们可能是:

myLength :: [a] -> Int

myLength中,正在向myLength的递归调用的结果中添加1,这是Int,这反过来导致Int。< / p>

splitLines :: [Char] -> [[Char]]

splitLines中,pre(一个[Char])被置于case语句的结果之前,从查看案例中,它是递归调用的结果至splitLines[[Char]];或一个空列表。在这两种情况下,预先pre(一个[Char])将依次产生[[Char]]

相关问题