递归函数
函数当然可以递归,而且更鼓励用递归而不是循环。
不过,递归需要关键字声明出来。
utop # let rec sum lst = match lst with |[]-> 0 |h::tail -> h + sum tail;; val sum : int list -> int = <fun> utop # sum [1;2;3;4];; - : int = 10
看另一个例子,去掉相邻的重复元素。
utop # let rec rr lst = match lst with | []-> [] | [x]-> lst | h1::h2::tail -> if h1=h2 then rr (h2::tail) else h1::rr tail;; val rr : 'a list -> 'a list = <fun> utop # rr [1;2;2;3;3;3;4;5];; - : int list = [1; 2; 3; 5]
选项
Option 是OCaml中常用的类型。它表示可能是某个值,或者可能什么都没有。
utop # let div x y = if y=0 then None else Some (x/y);; val div : int -> int -> int option = <fun> utop # div 6 3;; - : int option = Some 2 utop # div 6 0;; - : int option = None
从Option类型的值中取得有效值,需要用“解构”来提取。
op # let Some x = div 9 3;; Characters 4-10:
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched: None val x : int = 3
匹配成功,但编译器给出个语法上的警告,因为无法确定等号右边返回的就是个Some x 的形式,还可能是None呢。
局部绑定
utop # let x=5 in x * 2 + 1;; - : int = 11
x=5 的绑定只在in后的语句中有效。x并不是全局可访问的。
局部绑定是个表达式,最终返回一个值,就是in语句返回的值。
当然,局部绑定是可以嵌套的。
utop # let x = 5 in let y = 6 in x * x + y * y;; - : int = 61
记录和变体
utop # type pp = {x:int; y:int};; type pp = { x : int; y : int; }
记录与tuple相比,每个域有有个名字,这样就可以不按顺序给出每个域的值。
utop # let a = {y=3;x=4};; val a : pp = {x = 4; y = 3}
当然,可以采用解构赋值(更准确说,叫绑定)。
utop # let {x=vx} = a;; val vx : int = 4
解构也可出现在参数的位置。
op # let f {x=vx;y=vy} = vx*vx+ vy*vy;; val f : pp -> int = <fun>
utop # f {x=2;y=5};;
- : int = 29
用在参数位置,可以简化。
utop # let f {x;y} = x*x+y*y;; val f : pp -> int = <fun>
可以像Option一样,新的类型也可以组合自多个类型。
utop # type ppp = {x:int;y:int;z:int};; type ppp = { x : int; y : int; z : int; } utop # type p2p3 = P2 of pp | P3 of ppp;;
type p2p3 = P2 of pp | P3 of ppp utop # let g p = match p with P2 {x;y} -> "p2" | P3 {x;y;z} -> "p3";; val g : p2p3 -> string = <fun>
数组
op # let a = [|1;2;6;9|];; val a : int array = [|1; 2; 6; 9|] utop # Array.length a;; - : int = 4 utop # a.(2) <- 100;; - : unit = () utop # a;; - : int array = [|1; 2; 100; 9|]
Ocaml 鼓励不使用变量的编程。而数组中的元素是可变的。
但Ocaml也提供了面向可变性编程,面向过程编程的手段。比如数组,for循环。
记录类型的值默认是不能更改的。但可以用mutable来修饰,变成可以修改的类型。
# type r1 = {mutable x:int; mutable y:int};; type r1 = { mutable x : int; mutable y : int; } utop # let a = {x=5;y=10};; val a : r1 = {x = 5; y = 10} utop # a.x <- 100;; - : unit = () utop # a;; - : r1 = {x = 100; y = 10}
Ref
如果记录中只有一个字段,其用法就如同其它语言中的普通变量。因为这种用法比较有代表性,Ocaml就预先做了个类型。测一下:
utop # let a = {contents=10};; val a : int ref = {contents = 10}
OCaml提供了预定义的一些函数(或运算符),使得操作更简单直观。
utop # let x = ref 0;; val x : int ref = {contents = 0} utop # !x;; - : int = 0 utop # x := !x + 1;; - : unit = () utop # !x;; - : int = 1
for与while循环
utop # for i=1 to 10 do print_int i done;;
utop # while !p<10 do p := !p + 1; print_int !p done;;