【PL理论深化】(6) Ocaml 语言: 函数 | 匿名函数 | 函数可以接受多个参数 | OCaml 是一种将函数视为 first-class 的语言

​​​​​​​

  • 💬 写在前面:本章我们继续介绍如何使用 OCaml 进行函数式编程。介绍如何使用 let 定义函数,讲解匿名函数,函数可以接受多个参数,以及讨论 OCaml 将函数视为 first-class。

目录

0x00 函数

0x01 匿名函数(anonymous function)

0x02 函数可以接受多个参数

0x03 OCaml 是一种将函数视为 first-class 的语言


0x00 函数

定义函数时也是使用 let,下面我们来定义一个计算开方的函数 square :

x:= x^2

# 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 中,函数调用表达式通常写作 e_1,e_2 的形式,其中 e_1 和 e_2 可以是任意的表达式。

例如在上面调用 square 的三个例子中,e_1 都是 square,而 e_2 分别是 2, 2 + 5, square 2。

e_1 也可以直接是匿名函数,例如:

# (fun x -> x * x) 3;;
- : int = 9
# (fun x -> if x > 0 then x + 1 else x * x) 1;;
- : int = 2

0x02 函数可以接受多个参数

例如,我们可以编写一个函数,它接受两个整数 x 和 y 作为参数,并返回它们各自平方的和。

# 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

定义了变量 f 为 sum_of_squares 3 的值,其定义为一个接受整数 y 并返回函数:

(\textrm{square}\,\, \, 3)+(\textrm{square}\,\, \, y)

因此,可以通过 f4 调用来获得其值,结果为 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 是一个函数,接受整数 x 作为参数,如果 x 是偶数则返回 true。

换句话说,even 的定义体 x mod 2 是一个布尔表达式,用于计算 x 除以 2 的余数是否为 0。

函数 sum 接受三个参数 f, a, b,其中 f 是一个从整数到布尔值的函数。

这个函数应用于 a 和 b,并在计算结果为 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

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平渊道人

喜欢的话可以支持下我的付费专栏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值