练习

大多数练习的解决方案都可以获得。2022 年秋季是这些解决方案的首次公开发布。尽管康奈尔大学的学生已经可以获得这些解决方案几年了,但更广泛的传播将揭示可以进行改进的地方是不可避免的。我们很乐意添加或更正解决方案。请通过 GitHub 进行贡献。

Exercise: values [★]

以下是每个 OCaml 表达式的类型和值是什么?

  • 7 * (1 + 2 + 3)

  • "CS " ^ string_of_int 3110

提示:将每个表达式输入到顶层,它会告诉你答案。注意: ^ 不是指数运算。

Exercise: operators [★★]

查看 OCaml 手册中所有运算符的表格(您需要向下滚动以在该页面上找到它)。

  • 写一个表达式,将 42 乘以 10
  • 编写一个表达式,将 3.14 除以 2.0 。提示:在 OCaml 中,整数和浮点运算符的写法不同
  • 编写一个表达式,计算 4.2 的七次方。注意:OCaml 中没有内置的整数指数运算符(顺便说一句,在 C 语言中也没有),部分原因是大多数 CPU 不提供这种操作。

Exercise: equality [★]

  • 编写一个表达式,使用结构相等性比较 4242
  • 编写一个表达式,使用结构相等性比较 "hi" 和 "hi" 。结果是什么?
  • 编写一个表达式,使用物理相等性比较 "hi" 和 "hi" 。结果是什么?

Exercise: assert [★]

  • assert true;; 输入到 utop 中,看看会发生什么。
  • assert false;; 输入到 utop 中,看看会发生什么。
  • 编写一个表达式,断言 2110 不等于 3110(在结构上)。

Exercise: if [★]

编写一个 if 表达式,如果 2 大于 1 ,则评估为 42 ,否则评估为 7` 。

Exercise: double fun [★]

利用上面的增量函数作为指导,定义一个函数 double ,它将其输入乘以 2。例如, double 7 将是 14 。通过将其应用于一些输入来测试您的函数。将这些测试用例转换为断言。

Exercise: more fun [★★]

  • 定义一个函数,计算浮点数的立方。通过将该函数应用于几个输入来测试它
  • 定义一个函数,计算整数的符号(10-1)。使用嵌套的 if 表达式。通过将其应用于几个输入来测试您的函数。
  • 定义一个函数,根据给定的半径计算圆的面积。使用 assert 测试您的函数

对于后者,请记住浮点运算并不精确。与其断言一个确切的值,你应该断言结果是“足够接近”,例如,误差在 1e-5 之内。如果这对你来说是陌生的,值得阅读关于浮点运算的相关内容

一个接受多个输入的函数可以通过在 let 定义的一部分提供这些输入的附加名称来定义。例如,以下函数计算三个参数的平均值:

let avg3 x y z = (x +. y +. z) /. 3.

Exercise: RMS [★★]

定义一个函数,计算两个数字的均方根,即 \( \sqrt{\frac{x^2 + y^2}{2}} \) 。用 assert 测试您的函数。

Exercise: date fun [★★★]

定义一个函数,该函数接受一个整数 d 和一个字符串 m 作为输入,并在 dm 形成有效日期时返回 true 。在这里,有效日期的月份必须是以下缩写之一:Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec。日期必须是介于 1 和该月最少天数之间的数字,包括边界值。例如,如果月份是 Jan,则日期介于 131 之间,包括边界值;如果月份是 Feb,则日期介于 128 之间,包括边界值。

你的函数可以有多简洁(即代码行数少且短)?你肯定可以做到少于 12 行。

Exercise: fib [★★]

定义一个递归函数 fib : int -> int ,使得 fib n 是斐波那契数列中的第 n 个数字,该数列为 1, 1, 2, 3, 5, 8, 13, ... 即:

  • fib 1 = 1,
  • fib 2 = 1, and fib 2 = 1
  • fib n = fib (n-1) + fib (n-2) for any n > 2.

在顶层测试您的函数。

Exercise: fib fast [★★★]

您的 fib 实现计算第 50 个斐波那契数的速度有多快?如果计算几乎是瞬间完成的,那么恭喜!但大多数人最初想到的递归解决方案似乎会无限期地挂起。问题在于明显的解决方案会重复计算子问题。例如,计算 fib 5 需要同时计算 fib 3fib 4 ,如果这些分开计算,实际上会重复大量工作(指数级别的工作量)。

创建一个函数 fib_fast ,只需要线性数量的工作。提示:编写一个递归辅助函数 h : int -> int -> int -> int ,其中 h n pp p 定义如下:

  • h 1 pp p = p,
  • h n pp p = h (n-1) p (pp+p) 对于任何 n > 1

h 的概念是假设前两个斐波那契数是 ppp ,然后计算向前 n 个数字。因此, fib n = h n 0 1 对于任何 n > 0

n 的第一个值是多少,使得 fib_fast n 为负,表明整数溢出发生了?

Exercise: poly types [★★★]

以下每个函数的类型是什么?您可以要求顶层检查您的答案。

let f x = if x then x else x
let g x y = if y then x else x
let h x y z = if x then y else z
let i x y z = if x then y else y

Exercise: divide [★★]

编写一个函数 divide : numerator:float -> denominator:float -> float 。应用您的函数。

练习:结合律 [★★]

设我们已经定义了 let add x y = x + y 。以下哪个会产生一个整数,哪个会产生一个函数,哪个会产生一个错误?做出答案,然后在 toplevel 中检查你的答案。

  • add 5 1
  • add 5
  • (add 5) 1
  • add (5 1)

Exercise: average [★★]

定义一个中缀运算符 +/. 来计算两个浮点数的平均值。例如,

  • 1.0 +/. 2.0 = 1.5
  • 0. +/. 0. = 0.

Exercise: hello world [★]

utop 中键入以下内容:

  • print_endline "Hello world!";;
  • print_string "Hello world!";;

注意每个输出之间的差异。