2

我在过去曾经涉足过Haskell,最近又重新回到了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' 

是如何工作的,什么是预先真正被连接呢?我不明白案例表达式的结果是如何与pre连接的。也许我只需要有人详细解释这些功能的评估。我必须错过一些非常重要的事情。

在此先感谢!

编辑:我知道,这是一个复制粘贴失败。抱歉。编辑2:看来我的困惑是这些功能实际上是什么/返回/我现在已经全部解决了。感谢答案的人,它终于点击了!我很感激!

回答

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或\ r或\ r \ n开头。我们想要删除这些。所以我们使用模式匹配。看看其余变量是如何基本上是suf变量减去初始换行符(s)。

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

可视化,假设你有字符串“foo \ nbar \ r \ 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 

这是最终结果。

4

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

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

根据这个定义,一个空列表的myLength为0。(x:xs)彭定康解包列表到的第一个项目, a,以及其余项目列表xs。如果列表中有一个项目,那么xs是一个空列表,所以结果是1 + 0.依此类推。

递归最容易理解,首先查看基本情况,然后查看每个递归级别如何构建结果。 (基体的情况是,该函数不会调用自身的情况下,如果一个递归函数不具有基的情况下,输出将是无限的。)

在第二示例中,基座壳体(最后一种情况下在case-statment中)也是一个空的列表。所以pre会永远被添加到列表中,这将产生一个新的,更长的列表。

2

回复:myLength (x:xs) = 1 + myLength (xs) - 这是myLength定义的“半壁江山”,它说,通过模式匹配,即如果的说法有头有尾,那么结果是比递归尾部多了一个呼吁尾巴 - 需要有另一半说,结果是0时,该参数无法比拟x:xs,即当参数是一个空列表。

在第二种情况下,不同的图案匹配的可能性就只是通过case制成多一点epxlicit。

BTW,懒惰是不是一个关键的问题在这里 - ML,充满渴望的评估,但模式匹配很像Haskell中,将工作非常相似。貌似模式匹配是你真正需要刷一下。所有的第一个例子中的

+0

感谢您的回答。我有模式匹配下来压缩,我递归很好。我的错误在于这些函数实际上是/返回/。 – Rayne 2009-05-26 02:43:42

2

首先应该是这样的(编辑:它看起来像你现在修好了):

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

它的工作原理是这样的:说我给它有三个项目的名单,它返回一个加上尾巴的长度(这是一个加上尾巴的长度(这是一个加上尾巴的长度,这是[]在这一点上),这是1),这是W),其中是3(最终答案)。也许嵌套括号会帮助你理解它。 ;-)

1

看看函数的类型签名是什么会很有意义。他们可能是:

myLength :: [a] -> Int 

myLength,1被添加到递归调用的结果myLength,这是一个Int,这在一个Int反过来结果。

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

splitLinespre(一个[Char])被预先考虑的情况下语句,该语句,从看的情况下,可以是一个递归调用splitLines,这是[[Char]]的结果的结果;或一个空的列表。在两种情况下,预先输入pre(a [Char])将导致[[Char]]轮流。

+1

好的,我理解myLength的例子。但在本书中,它表示:(pre :)的第二个参数是case表达式的结果,但case表达式的结果是一个空列表(我得到)或递归调用splitLines,如何预先添加到任何东西?我正在努力解决这个问题。对不起,成为一个白痴。 :\ – Rayne 2009-05-25 21:19:23

+0

更多,我想我问的是,如何递归调用splitLines,导致[[Char]]? – Rayne 2009-05-25 21:20:51

相关问题