发生了什么?
我找到了一个f#递归函数的基本示例,该函数接受一个列表并返回仅包含偶数整数的列表。 我大部分都明白这一点,但有一点我很困惑。
let numbers = [1..4]
let rec even ls =
match ls with
| [] -> []
|head :: tail when head % 2 = 0 -> head :: even tail
|_::tail -> even tail
与头相匹配的线让我感到困惑。 这是我读的。 当头部均匀时追加头尾,然后再打even tail 。 因为我们头尾追尾,难道不会被一次又一次地加入头部的循环中吗?
另外,最后一行_::tail我假设的意思是“什么都不做,再次递归”,但是我在f#中查找了operator _ ,并且它说这是一个通配符模式。 这实际上是否意味着如果我的前两场比赛中没有任何一场比赛报道,请做这件事?
希望有人能澄清! f#看起来很有趣,但是来自必要的背景很难理解。
这是一个模式匹配表达式。 阅读每一行的方法是:在箭头的左侧->是如何检查数据的说明,箭头的右侧是检查结果的说明。
将其应用于您的案例,请遵循评论(为了清晰起见,我插入了一些换行符):
match ls with // Look at `ls`
| [] -> // when `ls` is an empty list,
[] // return an empty list
| head :: tail // when `ls` is a `head` attached to a `tail`,
when head % 2 = 0 -> // AND `head` is an even number,
head :: even tail // call `even tail`, attach `head` to it, and return that result
| _::tail -> // when `ls` is "something" attached to a `tail`,
even tail // call `even tail` and return that result
请注意,最后一种情况适用于第二种情况适用的所有情况。 这种不明确性可以通过案例的顺序来解决:程序将依次依次匹配(查看,检查)数据,并执行匹配的第一个案例的代码。
你似乎错过了另一个微妙点:不变性。 该列表是不可变的。 您无法“更新”(“更改”,“更改”)列表。 如果你有一个列表,它会一直保持这种状态,编译器保证它。 相反,数据通过程序演变的方式是通过创建基于旧数据的新数据。 特别是,如果你说a :: b ,这不会修改列表b ,而是会创建一个新列表,其中a是head而b是tail。
这些都是F#中非常基本的概念,而且你错过了它们的事实告诉我,你只是刚开始看语言(也许在一般的函数式编程中)。 如果这是真的,我建议先阅读一些介绍性材料,以熟悉基本概念。 我最喜欢的是fsharpforfunandprofit.com,我无法推荐它。
追加head到tail的时候head是偶数,则调用even tail一次。 因为我们追加head到tail ,那不是正好被卡在一遍又一遍的增加头部的循环?
关闭但不完全。 它是通过递归even tail返回的列表的head 。 每次递归时,这个模式匹配表达式中的tail值都有一个较少的项。
另外,最后一行_::tail我假设的意思是“什么都不做,再次递归”,但是我在F#中查找了operator _ ,并且它说这是一个通配符模式。 这实际上是否意味着如果我的前两场比赛中没有任何一场比赛报道,请做这件事?
_可以是通配符模式,但是在这种模式匹配的例子中,它仅仅表明当我们解构列表时我们不关心头部值; 我们只关心tail 。 这个子句(因为它是在测试均匀性的子句之后出现)导致非偶数头值被丢弃。
您正在处理不可变数据类型(不可变列表)。 这意味着列表不会更改,每个操作都会创建并返回一个新列表。
模式匹配是将数据解构为多个部分的一种方式。 举个例子。 如果你有清单[1;2;3;4]并且你写了。
let list = [1;2;3;4]
let (head :: tail) = list
那么head是值1 。 tail是列表[2;3;4] , list仍然是[1;2;3;4] 。
让我们一步一步地通读您的示例。
even [1;2;3;4]被调用。 然后有三种模式匹配的情况。 第一个检查[1;2;3;4]是否为空列表。 它不是那么检查下一个。 head :: tail将列表提取为如上所示的两部分。 因此, head是1 , tail代表[2;3;4] ,但是when head % 2 = 0添加了条件。 它检查head (目前是1 )是否可以分为两部分。 它不是,所以它进入最后的模式匹配。
最后一个是_::tail 。 首先它和head::tail完全一样。 它提取第一个值1并将其存储在变量_ 。 使用_作为变量名的原因是为了澄清从未使用名字。 你也可以改变_来head ,代码的作用是一样的。
最后的模式匹配匹配,现在你有1存储在_和[2;3;4]存储在tail 。 而你所做的就是打电话给even tail 。 或者在这种情况下,您even [2;3;4]可以调用even [2;3;4]并返回此函数调用的结果作为结果。
当even [2;3;4]被调用时,它的确如上所述。
它检查它是否为空列表。 不是。 然后它提取head的第一个值2和[3;4]到tail 。 它检查何时条件。 这一次是真的。 所以现在它执行head :: even tail
或者,如果我们将值替换为2 :: even [3;4]
::是一个列表的连接,但在我们可以连接一个列表之前,首先函数调用even [3;4]需要返回一个列表。 所以even [3;4]被调用。
然后再检查。
head价值3由2整除都能跟得上。 3到_和[4]到tail , even [4]调用even [4] 。 even [4]也是如此:
[4]是一个空的列表。 不。 4分配给head偶数? 是的。 所以4 :: even []被调用。 even []然后也是这样:
[]是一个空列表。 是的。 所以返回空列表。 [] 然后它倒退。
-> means "returns"
even [] -> []
4 :: even [] -> [4]
even [3;4] -> [4]
2 :: even [3;4] -> [2;4]
even [2;3;4] -> [2;4]
even [1;2;3;4] -> [2;4]
链接地址: http://www.djcxy.com/p/80561.html
上一篇: What's happening?
