Recursion 谁能给我解释一下这些Haskell函数吗?
我过去曾涉猎过Haskell,最近又认真地重新涉猎,我正在阅读现实世界中的Haskell。他们所展示的一些例子,我还没有理解。在这一点上:Recursion 谁能给我解释一下这些Haskell函数吗?,recursion,functional-programming,lazy-evaluation,haskell,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
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'
我不明白这是怎么回事,1到底是怎么被添加的呢?递归如何返回可以添加到的内容?我不明白
这里我们有一个:
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真正附加了什么?我不明白case表达式的结果是如何连接到pre的。也许我只需要有人详细解释一下这些函数的计算。我一定错过了一些非常重要的事情
提前谢谢
编辑:我知道,这是复制粘贴失败。对不起
编辑2:似乎我对这些函数的实际用途感到困惑/返回/我现在已经全部解决了。谢谢大家的回答,终于点击了!我很感激 我认为
myLength
的定义忽略了列表为空的情况:
myLength [] = 0
myLength (x:xs) = 1 + myLength (xs)
根据此定义,空列表的myLength
为0。(x:xs)
模式将列表解压为第一项a
,以及包含其余项的列表xs
。如果列表有一项,那么xs
是一个空列表,因此结果为1+0。等等
当您首先查看基本情况,然后查看每个级别的递归如何基于结果构建时,递归最容易理解。(基本情况是函数不调用自身的情况。如果递归函数没有基本情况,输出将是无限的。)
在第二个示例中,基本案例(案例陈述中的最后一个案例)也是一个空列表。因此pre总是被附加到一个列表中,这将产生一个新的、更长的列表。Re:myLength(x:xs)=1+myLength(xs)
——这是myLength
定义的“一半”,它通过模式匹配表示,如果参数有一个头和一个尾,然后,结果比尾部的递归尾部调用多了一个——当参数不能匹配x:xs
时,也就是说,当参数是空列表时,需要有另一半来表示结果为0
在第二种情况下,通过case
使不同模式匹配的可能性更大一些
顺便说一句,懒惰在这里不是一个关键问题——ML与Haskell非常相似,具有热切的评估,但模式匹配。看起来模式匹配是您真正需要复习的内容。首先,第一个示例应该是这样的(编辑:看起来您现在已经修复了它):
它的工作原理是这样的:假设我给它一个包含三个项目的列表,它返回一个加上尾巴的长度(这是一个加上尾巴的长度(这是
[]
),这是1),这是w),这是3(最终的答案)。也许嵌套括号可以帮助您理解它。;-) 看看函数的类型签名是什么很有启发性。它们可以是:
myLength :: [a] -> Int
在myLength
中,1被添加到对myLength
的递归调用的结果中,这是一个Int
,这反过来会导致一个Int
splitLines :: [Char] -> [[Char]]
在
splitLines
中,pre
(一个[Char]
)被添加到case语句的结果之前,从案例来看,case语句是递归调用splitLines
的结果,即[Char]
;或者一个空的列表。在这两种情况下,前置pre
(a[Char]
)将依次导致[[Char]]]
。对于第一种情况,这是一种非常基本的递归方法。然而,它似乎缺少一个部分:
myLength [] = 0
它的工作原理是一次从列表中缩小一个元素,然后将一个元素添加到结果中。为了可视化,考虑调用< /P>
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开头。我们想删除这些。所以我们使用模式匹配。了解rest变量本质上是suf变量减去初始换行字符的方式
我们有pre,这是第一行,还有rest,这是文本的其余部分。因此,为了继续将rest拆分为行,我们递归地调用它的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
这是最终结果。好的,我理解myLength示例。但是在书中,它说:(pre:)的第二个参数是case表达式的结果,但是case表达式的结果要么是一个空列表(我得到的),要么是对splitLines的递归调用,pre是如何添加到任何东西的?我正试着在脑海里解决这个问题。对不起,我是个白痴\另外,我想我要问的是,对splitLines的递归调用如何产生[[Char]]?你知道(:)是“cons”操作符,对吧;它接受一个元素和一个列表,并返回一个列表,其中该元素位于另一个列表的前面。所以(:)的类型是“a->[a]->[a]”。因为pre有[Char]类型,所以splitLines的结果有[Char]类型。我理解这是如何工作的,但现在我不明白为什么会这样。结果要么是另一个递归调用,要么是一个空列表。从本质上看,每次重复都会将pre连接到pre,但case表达式不会返回pre。令人困惑。事实上,在再次阅读之后,pre似乎连接到splitLines的参数,而不是结果。我只是不知道这是怎么回事