- 💬 写在前面:本章我们继续介绍如何使用 OCaml 进行函数式编程。介绍如何使用 let 定义函数,讲解匿名函数,函数可以接受多个参数,以及讨论 OCaml 将函数视为 first-class。
目录
0x00 函数
0x01 匿名函数(anonymous function)
0x02 函数可以接受多个参数
0x03 OCaml 是一种将函数视为 first-class 的语言
0x00 函数
定义函数时也是使用 let,下面我们来定义一个计算开方的函数 square
:
# let square x = x * x;;
val square : int -> int = <fun>
在函数的名字后面写上函数的参数的名字,然后在等号后面定义函数的主体。
函数 square 定义了一个接受参数 x 并返回 x * x 作为结果的函数。
定义函数后,OCaml 解释器会告诉你该函数的类型。
在这个例子中,int -> int 表示这是一个以整数为参数并返回整数的函数。
上面提到的 <fun> 表示定义的值是一个函数。对于函数,OCaml 解释器不会显示具体的值。
定义函数之后,可以像下面这样调用并使用它:
# square 2;;
- : int = 4
# square (2 + 5);;
- : int = 49
# square (square 2);;
- : int = 16
.
我们也可以用如下方式定义 square 函数:
# let square = fun x -> x * x;;
val square : int -> int = <fun>
在这里,`fun x -> x * x` 表示一个接受参数 x 并返回 x * x 的函数,
而名字 square 用来指代这个函数值。这样定义函数的方式与前面定义的方式完全相同。
可以认为编译器在内部将 `let square x = x * x` 处理为 `let square = fun x -> x * x`。
0x01 匿名函数(anonymous function)
通过 `fun x -> x * x` 定义的函数被称为 匿名函数 (anonymous function)。
举另一个例子,定义一个检查参数 x 是否为负数的函数 neg。
在这个例子中,函数的主体是一个条件表达式。
# let neg x = if x < 0 then true else false;;
val neg : int -> bool = <fun>
# neg 1;;
- : bool = false
# neg (-1);;
- : bool = true
在 OCaml 中,函数调用表达式通常写作 的形式,其中 和 可以是任意的表达式。
例如在上面调用 square 的三个例子中, 都是 square,而 分别是 2, 2 + 5, square 2。
也可以直接是匿名函数,例如:
# (fun x -> x * x) 3;;
- : int = 9
# (fun x -> if x > 0 then x + 1 else x * x) 1;;
- : int = 2
0x02 函数可以接受多个参数
例如,我们可以编写一个函数,它接受两个整数 和 作为参数,并返回它们各自平方的和。
# let sum_of_squares x y = (square x) + (square y);;
val sum_of_squares : int -> int -> int = <fun>
# sum_of_squares 3 4;;
- : int = 25
OCaml 告诉我们,该函数的类型是 `int -> int -> int`,这意味着它接受两个整数并返回一个整数。
或者,将类型解释为 `int -> (int -> int)`,这意味着它接受一个整数参数,
并返回一个从整数到整数的函数,实际上,可以这样解释和使用上述定义的函数:
# let f = sum_of_squares 3;;
val f : int -> int = <fun>
# f 4;;
- : int = 25
定义了变量 为 sum_of_squares 3 的值,其定义为一个接受整数 并返回函数:
因此,可以通过 调用来获得其值,结果为 25。
定义递归函数的方法也是相同的,但在 let 后面必须加上关键字 rec。
例如,可以如下定义阶乘函数 factorial:
# let rec factorial a =
if a = 1 then 1 else a * factorial (a - 1);;
val factorial : int -> int = <fun>
# factorial 5;;
- : int = 120
0x03 OCaml 是一种将函数视为 first-class 的语言
OCaml 是一种将函数视为 first-class 的语言,这意味着定义和使用函数的方式非常灵活。
在编程语言中,要使一个值称为 first-class,必须能将其存储在变量中,
作为函数的参数传递,并且能作为函数的返回值返回。
例如,在 C 语言中,整数, 字符等值被视为 first-class。
在 OCaml 中,函数也被视为 first-class。
因此可以像下面这样,一个函数可以接受另一个函数作为参数:
# let sum f a b =
(if f a then a else 0) + (if f b then b else 0)
val sum : (int -> bool) -> int -> int -> int = <fun>
# let even x = x mod 2 = 0;;
val even : int -> bool = <fun>
# sum even 3 4;;
- : int = 4
# sum even 2 4;;
- : int = 6
在上述中,even 是一个函数,接受整数 作为参数,如果 是偶数则返回 true。
换句话说,even 的定义体 x mod 2 是一个布尔表达式,用于计算 除以 2 的余数是否为 0。
函数 sum 接受三个参数 ,其中 是一个从整数到布尔值的函数。
这个函数应用于 和 ,并在计算结果为 true 时返回它们的和。
例如,sum even 3 4 中,我们将函数 even 作为 sum 的第一个参数传递,因此返回值为 4。
函数也可以返回另一个函数:
# let plus a = fun b -> a + b;;
val plus : int -> int -> int = <fun>
# let plus1 = plus 1;;
val plus1 : int -> int = <fun>
# plus1 1;;
- : int = 2
# plus1 2;;
- : int = 3
# let plus2 = plus 2;;
val plus2 : int -> int = <fun>
# plus2 1;;
- : int = 3
函数 plus 接收一个参数 a,返回一个函数 fun b -> a + b 。
类型 int -> int -> int 可以解释为 int -> (int -> int),
意味着它接收一个整数并返回一个从整数到整数的函数。
变量 plus1 被定义为 plus 1,因此 plus1 是一个接收参数 b 并返回 1 + b 的函数。
.
类似地,plus2 是一个接收整数参数并将其加上2的函数。
像 sum 和 plus 这样接受或返回其他函数的函数称为高阶函数。
如果编程语言支持高阶函数,我们就可以更抽象地思考和编程,
这样可以更方便地命名和抽象编程模式,从而编写更简洁易读的程序。
📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 2022.9.14
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!
📜 参考资料 - R. Neapolitan, Foundations of Algorithms (5th ed.), Jones & Bartlett, 2015. - T. Cormen《算法导论》(第三版),麻省理工学院出版社,2009年。 - T. Roughgarden, Algorithms Illuminated, Part 1~3, Soundlikeyourself Publishing, 2018. - J. Kleinberg&E. Tardos, Algorithm Design, Addison Wesley, 2005. - R. Sedgewick&K. Wayne,《算法》(第四版),Addison-Wesley,2011 - S. Dasgupta,《算法》,McGraw-Hill教育出版社,2006。 - S. Baase&A. Van Gelder, Computer Algorithms: 设计与分析简介》,Addison Wesley,2000。 - E. Horowitz,《C语言中的数据结构基础》,计算机科学出版社,1993 - S. Skiena, The Algorithm Design Manual (2nd ed.), Springer, 2008. - A. Aho, J. Hopcroft, and J. Ullman, Design and Analysis of Algorithms, Addison-Wesley, 1974. - M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997. - A. Levitin, Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003. - A. Aho, J. Hopcroft, and J. Ullman, Data Structures and Algorithms, Addison-Wesley, 1983. - E. Horowitz, S. Sahni and S. Rajasekaran, Computer Algorithms/C++, Computer Science Press, 1997. - R. Sedgewick, Algorithms in C: 第1-4部分(第三版),Addison-Wesley,1998 - R. Sedgewick,《C语言中的算法》。第5部分(第3版),Addison-Wesley,2002 |