1.4 1.5

1.4 1.5

pointeur

## 4.函数


正如我们之前提到过的,函数是OCaml语言的核心。接下来我们将学习如何创建以及调用函数。


### 创建函数


在OCaml中,我们使用关键词`let`来定义一个函数:


```ocaml

let f x y = x + y

```

为了更好理解代码行的含义,以下是在Python中的相同程序:

```python

def f(x, y):

  return x + y

```

在此我们标注几点不同:

- 参数并不写在括号中,而是直接写到函数名后面;

- 参数之间也不用逗号隔开,用空格分隔即可;

- 函数的主体从符号`=`开始;

- 函数主体只包含一个表达式,而不是一系列指令(instruction)。

- 唯一的指令(instruction)永远是函数返回的结果。


这种极简的句法在最初会看起来有些陌生,但我们会很快习惯它,并且当我们在OCaml中更进一步时会发现它很方便(参照附加部分)。


如果在OCaml编译器中输入以下命令,我们将会得到函数的类型:

```ocaml

f

```

我们可以看到这个函数的类型为:`int -> int -> int`。关于这个注释我们需要知道,前两个类型为参数的类型,而最后一个则是返回的值的类型。这里,参数`x`和`y`为`int`,它们的和也为`int`。如果你想了解为什么选择了这样不太直观的注释,我们将会在柯里化这一节中讨论。


一个实用的做法(在学校的考试中也是这样要求的)是明确批注出函数的类型。针对这一点,我们使用句法`(参数:类型)`来代替`参数`。对于返回的值,我们在`=`前使用`:type`来指出其类型。

```ocaml

let f (x : int) (y : int) : x + y

```

这个注释会更长一些,但它的优点是可以明确地显示出不同表达式的类型。同样,这也使得我们更容易理解这个函数,因为我们可以很清楚地看到参数以及返回的值可以取什么样的值。



### 小窍门:乘法的定义


我们也可以一次同时定义几个函数,只要用`and`(注意:与&&无关)来分开它们的声明即可。

```ocaml

let identite x = x and carre x = x * x and cube x = x * x * x

```

但是在此句法中,我们不能使用一行中靠前的函数来定义后面的函数。 比如,我们不能写成这样:

```ocaml

let carre x = x * x and cube x = (carre x) * x

```

因为当我们定义`cube`时,函数`carre`并不存在在环境中(还没有被运行)。


### 调用函数


现在我们拥有了第一个函数(不要犹豫去创建其它函数),来看一下我们是如何调用它的。

```ocaml

f 1 2

```

就这样!通常来讲,编译器将会回复`3`。

如果你需要使用更复杂的参数,那么你可以用括号括起来:

```ocaml

f (4 * 3) 7

```


### 以及变量?


事实上,变量的概念并不存在于OCaml中[^refs]。但是,我们可以有没有参数的函数常量。

```ocaml

let pi = 3.1415

```

此外,我们还可以使用表达式来限制函数,那么就可以仅仅使用本地方式来定义一个函数。对此,在声明完函数之后,我们加入关键词`in`以及一个表达式,这样我们就可以使用刚刚定义好的函数。

```ocaml

let cube x = x * x * x in (cube 2) + (cube 3)


(*在不定义cube的情况下,我们可以这样写:*)

(2 * 2 * 2) + (3 * 3 * 3)

```

通常我们会在`in`之后换行来使得代码更易于阅读。同理我们也可以嵌套`let`和`let/in`:

```ocaml

(*这个函数计算了平面中的范数,已知坐标*)

let norme (X : float) (y : float) : float =

  let carre a = a * a in

  let x_carre = carre x in

  let y_carre = carre y in

  sqrt (x_carre + y_carre) (*sqrt是OCaml中的基础函数*)

```


[^refs]: 事实上,这和'引用(reference)'的概念是类似的,但是我们会避免使用它,并且在本教程中也不会涉及。


## 5.课后练习


为了验证你是否已经理解了到目前为止的内容,你可以尝试回答下列问题。如果答对的话,你的回答会从红色变为绿色。

(因为编者是在网页上撰写的,但是掘金上并不支持这样的代码格式,所以译者稍加改动。可以打开开头的源网页来测试。)



下列表达式的类型分别是什么?如果其类型不成立,则输入`错误`。


- 2 + 2

  <details>

  <summary><答案点我></summary>

  int

  </details>


- 2e3 +. 2.5 *. (float_of_int 4)

  <details>

  <summary><点我></summary>

  float

  </details>

- 'H'

  <details>

  <summary><点我></summary>

  char

  </details>

- 12 > 3 && 7

  <details>

  <summary><点我></summary>

  错误

  </details>

- "你" ^ "好" ^ "!"

  <details>

  <summary><点我></summary>

  string

  </details>

- 42 > 12 || 19 > 38

  <details>

  <summary><点我></summary>

  bool

  </details>

- let ajouter x y = x + y

 ajouter (*我们询问的是ajouter的类型*)

  <details>

  <summary><点我></summary>

  int -> int -> int

  </details>

- 'A' + 3

  <details>

  <summary><点我></summary>

  错误

  </details>

- (*我们假设'x'和'y'是之前定义过的常量


  *但是我们并没有为其赋值

   

   

  *并且:这个表达式并不是一个错误类型

  *)

   

  x +. (y *. y)

  <details>

  <summary><点我></summary>

  float

  </details>

   

   

   

下列的布尔类型表达式是真(`true`)或假(`false`)?


- 42 > 12 || 19 > 38

  <details>

  <summary><点我></summary>

  true

  </details>

- 4 * 5 + 2 = 28

  <details>

  <summary><点我></summary>

  false

  </details>

- 'a' < 'e'

  <details>

  <summary><点我></summary>

  true

  </details>

- let x = 15

 x > 2 && x < 17

  <details>

  <summary></summary>

  true

  </details>

- "Zoé" > "Alice"

  <details>

  <summary><点我></summary>

  true

  </details>

   

   

如果你还想要尝试更多的练习,下列是一个真正的习题。

  实现下列函数:

   

  - 使一个浮点数`x`与其立方关联的函数;

   

  - 一个有三个参数的函数,其参数分别为`a`、`b`和`ε`,并使其分辩在给定的` ε`条件下,`a`是否等于`b`。为了简化这项任务,我们默认`a`总是大于`b`;

   

  - 该函数的参数为一个字符与一个整数,并确认在ASCII表中,该字符对应的编码是否与这个整数一致。你可以先测试以下字符`A`,其在ASCII表中对应编码为`65`。

   

   

   

  <details>

  <summary><点我></summary>

  

  

  

```ocaml

  let cube (x : float) : float = x * x * x

  

  

  

  let egal (a : float) (b : float) (epsilon : float) : bool =

  

    let diff = a -. b in

    

    diff < epsilon

  

  

  let est_ascii (c : char) (i : int) : bool =

    (int_of_char c) = i

    

 ```

  </details>




当你完成之后,你就可以进入下一章进行学习了。

如果你有很多问题的话,毫无疑问,最好的解决办法是重新阅读一遍第一章的内容,并且自己测试一下所有例子中的代码,然后自己再举一些另外的例子。

如果你觉得有些地方讲的不是很清楚,需要解释的话可以给这个邮箱地址发邮件。


`ocaml @ gelez . xyz`


Report Page