我是哈斯克尔的初学者,现在我发现了一个我不太了解的行为。 在我正在阅读的一本书中有一个练习来编写一个函数,该函数删除了给定char的第一个匹配并让其余的数组不加修改。 经过几次尝试,我想出了有效的代码,但我不知道为什么。代码如下:
removeFirst r [s]
| s == r = []
| otherwise = [s]
removeFirst r (x:xs)
| r /= x = x : removeFirst r xs
| otherwise = xs
可以请任何人向我解释为什么这个电话
removeFirst 'a' "strange"
而不是返回xs,因此" nge"?为什么连接发生以及代码应该如何获得" nge"?
答案 0 :(得分:8)
我们只考虑递归情况:
removeFirst r (x:xs)
| r /= x = x : removeFirst r xs
| otherwise = xs
通话顺序为:
removeFirst 'a' "strange"
:
自'a' /= 's'
起,评估结果为's' : removeFirst 'a' "trange"
removeFirst 'a' "trange"
:
自'a' /= 't'
起,评估结果为't' : removeFirst 'a' "range"
但我们之前的通话中已经有's':
,所以我们总是得到
's': ('t' : removeFirst 'a' "range")
removeFirst 'a' "range"
:
自'a' /= 'r'
起,评估结果为'r' : removeFirst 'a' "ange"
。
但我们之前的通话中已经有's':'t':
,所以我们总是得到
's' : ('t' : ('r' : removeFirst 'a' "ange")
removeFirst 'a' "ange"
:
由于'a' /= 's'
为false
,因此评估为xs
,在本例中为"nge"
。
但我们之前的通话中已经有's':'t':'r':
,所以我们总得's':'t':'r':"nge"
。
<强>更新强>
@Zeta在Pastebin提供了评估条款的精彩摘要,我在这里无耻地包括:
removeFirst 'a' "strange"
= 's' : removeFirst 'a' "trange"
= 's' : 't' : removeFirst 'a' "range"
= 's' : 't' : 'r' : removeFirst 'a' "ange"
= 's' : 't' : 'r' : "nge"
= "strnge"
答案 1 :(得分:2)
不是关于你的问题的答案(你已经得到了很好的答复),但是关于你的功能风格的评论:过于命令,不够功能/类型驱动。
当你编写一个处理列表(或任何递归类型)的函数时,总是先从基本情况(空列表,节点......)开始,然后考虑 inductive < / em>(递归) case(s)(这里有一个列表,可以是空列表,但我们不需要关心已经在基地检查过情况)。
考虑到这一点,当我们查看您的代码时,我们首先注意到您的模式匹配并非详尽无遗,因为您忘记了基本情况,空列表情况:
removeFirst _ [] = []
-- removing the first matching element of an empty list is just
-- returning that empty list
和你的2个模式匹配是一样的,第一个是第二个写的颠倒,专门用于一个项目长列表;重写以比较两者:
removeFirst r (x:[]) -- removeFirst r (x:xs)
| x /= r = x : [] -- | x /= r = x : removeFirst r xs
| otherwise = [] -- | otherwise = xs
最后,你可以写:
removeFirst _ [] = [] -- "_" as we don't care about the character to remove
removeFirst r (x:xs)
| x == r = xs
| otherwise = x : removeFirst r xs
(只是一个品味问题:我更喜欢在开头和递归的情况下将非递归情况分组到最后。)