Дмитрий Ярушин Asked:2020-12-13 00:13:26 +0000 UTC2020-12-13 00:13:26 +0000 UTC 2020-12-13 00:13:26 +0000 UTC 请解释一下表达方式。F# 772 请解释一下表达方式。F# let rec fact n = iff ((=) n 1) 1 ((*) n (fact ((-) n 1)));; f# 2 个回答 Voted Best Answer Fyodor Soikin 2020-12-13T01:19:38Z2020-12-13T01:19:38Z 关键字let指定某物的名称。大多数情况下,它为值定义一个名称: let x = 5 // числу 5 даём имя "x" let y = "abc" // строке "abc" даём имя "y" 它let大致翻译成俄语为“让”(在数学意义上): пусть х = 5 пусть y = "abc" 另一种情况是名称的定义不是针对值,而是针对函数。在这种情况下,紧跟在单词之后let,指示函数的名称,然后是参数,以空格分隔: let f x = x + 5 这一行定义了一个函数f,它有一个参数x,函数的值是通过将参数的值加五来计算的。 这个定义中的关键字rec表示该函数是递归的——也就是说,在计算它的值时,它自己使用。例如: let rec f x = if x = 1 then 1 else x*(f (x-1)) 1当参数为 时,此函数有一个值x=1,否则它的值是通过调用此函数本身并少一个参数来计算的。 你的函数体使用了另一个函数iff。这个函数不在 F# 标准库中,所以我只能假设它是在代码中更高层的某个地方定义的。 这个函数有三个参数: (=) n 1 1 (*) n (fact ((-) n 1)) 第二个参数只是常量“1”,没什么特别的。但是第一个和第三个参数是表达式,其中以前缀表示法调用运算符函数(而不是像通常的运算符那样以中缀表示)。在 F# 中,这是合法的并且经常使用。例如,(=) n 1- 与n = 1. 这里的运算符=用于前缀表示法,必须用括号括起来。相似地: (-) n 1- 与n - 1 (*) x y- 与x * y 因此,复数符号(*) n (fact ((-) n 1))与n * fact (n-1). 所以你的整个函数可以这样重写: let rec fact n = iff (n=1) 1 (n * fact (n-1)) 请注意,此 c 表示法iff 不等同于 的类似表示法if .. then。它与计算函数参数的顺序有关。F# 使用参数求值的应用顺序——即所有参数的值在调用函数之前求值。在这种情况下,这意味着两个值n=1和都将在调用函数之前n * fact(n-1)进行评估,这意味着调用将在任何情况下执行,无论表达式是否为真。这将导致无限递归,最终导致堆栈溢出。ifffact(n-1)n=1 从表达式的形式来看,我假设它的作者打算模仿 Lisp - 因此是运算符的前缀符号。这就是 Lisp 中的函数的样子: (defun f (n) (if (= n 1) 1 (* (f (- n 1))))) 但是有一个重要的区别:ifLisp 中的表单,就像if .. thenF# 中的构造一样,不是普通的函数,而是所谓的“特殊形式”。当处理特殊形式时,编译器以一种特殊的方式表现 - 特别是,当处理 form 时,if编译器不会评估所有三个参数,而是首先评估第一个,然后是第二个或第三个(但不是同时在同时)。这在您的示例中不会发生,因为它iff只是一个常规函数,这将导致堆栈溢出。 Дмитрий Ярушин 2020-12-13T00:54:14Z2020-12-13T00:54:14Z 从人的角度来看,它看起来像这样: let rec fact n = if n=1 then 1 else n*fact(n-1) 如果我们加上fact 5,那么它会计算 5 的阶乘。 来找我)
关键字
let指定某物的名称。大多数情况下,它为值定义一个名称:它
let大致翻译成俄语为“让”(在数学意义上):另一种情况是名称的定义不是针对值,而是针对函数。在这种情况下,紧跟在单词之后
let,指示函数的名称,然后是参数,以空格分隔:这一行定义了一个函数
f,它有一个参数x,函数的值是通过将参数的值加五来计算的。这个定义中的关键字
rec表示该函数是递归的——也就是说,在计算它的值时,它自己使用。例如:1当参数为 时,此函数有一个值x=1,否则它的值是通过调用此函数本身并少一个参数来计算的。你的函数体使用了另一个函数
iff。这个函数不在 F# 标准库中,所以我只能假设它是在代码中更高层的某个地方定义的。这个函数有三个参数:
(=) n 11(*) n (fact ((-) n 1))第二个参数只是常量“1”,没什么特别的。但是第一个和第三个参数是表达式,其中以前缀表示法调用运算符函数(而不是像通常的运算符那样以中缀表示)。在 F# 中,这是合法的并且经常使用。例如,
(=) n 1- 与n = 1. 这里的运算符=用于前缀表示法,必须用括号括起来。相似地:(-) n 1- 与n - 1(*) x y- 与x * y因此,复数符号
(*) n (fact ((-) n 1))与n * fact (n-1).所以你的整个函数可以这样重写:
请注意,此 c 表示法
iff不等同于 的类似表示法if .. then。它与计算函数参数的顺序有关。F# 使用参数求值的应用顺序——即所有参数的值在调用函数之前求值。在这种情况下,这意味着两个值n=1和都将在调用函数之前n * fact(n-1)进行评估,这意味着调用将在任何情况下执行,无论表达式是否为真。这将导致无限递归,最终导致堆栈溢出。ifffact(n-1)n=1从表达式的形式来看,我假设它的作者打算模仿 Lisp - 因此是运算符的前缀符号。这就是 Lisp 中的函数的样子:
但是有一个重要的区别:
ifLisp 中的表单,就像if .. thenF# 中的构造一样,不是普通的函数,而是所谓的“特殊形式”。当处理特殊形式时,编译器以一种特殊的方式表现 - 特别是,当处理 form 时,if编译器不会评估所有三个参数,而是首先评估第一个,然后是第二个或第三个(但不是同时在同时)。这在您的示例中不会发生,因为它iff只是一个常规函数,这将导致堆栈溢出。从人的角度来看,它看起来像这样:
如果我们加上
fact 5,那么它会计算 5 的阶乘。来找我)