Chapter 0+: OCaml
函数
OCaml 的函数也很简单,比如:
let sumsq_curried (x:int) (y:int) = x * x + y * y;;
let sumsq_uncurried ((x, y): int * int) = x * x + y * y;;
let currying2 (f: 'a -> 'b -> 'c) (x: 'a) (y: 'b) = f x y;;
let uncurrying2 (f: 'a * 'b -> 'c) ((x, y): 'a * 'b) = f (x, y);;
注意:对于递归函数,我们需要显式地写出。如下所示:
- 因为,OCaml 默认 the name of function is invisible in the scope of its own body.
模式匹配
更加复杂度例子,考虑我们希望将 string
转换成 char list list
,同时不希望任何空列表出现在我们的程序中:
let rec string_to_char_list (s: string) =
match s with
| "" -> []
| x -> (String.get x 0) :: (string_to_char_list (String.sub x 1 ((String.length x) - 1)))
let rec split_char_list l =
let rec loop (w: char list) l: char list list =
match w, l with
| [], [] -> [] (* When w is of [], we don't add w to the list*)
| _, [] -> w::[]
| [], ' '::xs -> loop [] (xs) (* When w is of [], we don't add w to the list*)
| _, ' '::xs -> w::(loop [] xs)
| _, x::xs -> loop (w@[x]) xs
in loop [] l
(* "Haiyaa! Uncle Roger disappointed~" ->
[['H'; 'a'; 'i'; 'y'; 'a'; 'a'; '!']; ['U'; 'n'; 'c'; 'l'; 'e'];
['R'; 'o'; 'g'; 'e'; 'r'];
['d'; 'i'; 's'; 'a'; 'p'; 'p'; 'o'; 'i'; 'n'; 't'; 'e'; 'd'; '~']] *)
let split_string (s: string) = split_char_list(string_to_char_list s)
自定义数据类型
对应着 Haskell 的
从而,我们可以写一个 evaluator:
let eval (a: ast) =
match a with
| ANum n -> n
| APlus x y -> (eval x) + (eval y)
| AMinus x y -> (eval x) - (eval y)
| ATimes x y -> (eval x) * (eval y);;
对应着 Haskell 的
eval :: AST -> Int
eval (ANum n) = n
eval (APlus x y) = (eval x) + (eval y)
eval (AMinus x y) = (eval x) - (eval y)
eval (ATimes x y) = (eval x) * (eval y)
unit
type
()
就是 unit type。在函数式编程语言中,unit type 如果用于返回值,那么一般而言,就是一个副作用函数
- Haskell 的 I/O Monad 就是
()
如果用于参数,那么
-
可能是一个耗时的计算,比如:
let f () = <long and complex calculation>;;
-
可能是 finalization code,如下。
let read file =
let chan = open_in file in
try
let nbytes = in_channel_length chan in
let string = String.create nbytes in
really_input chan string 0 nbytes;
close_in chan;
string
with exn ->
(* finalize channel *)
close_in chan;
(* re-raise exception *)
raise exn;;
我们希望,即使出现了 exception,finalization code 也可以正常执行。
我们可以简化为:
let read file =
let chan = open_in file in
(* finalization code *)
let finalize () = close_in chan in
try
let nbytes = in_channel_length chan in
let string = String.create nbytes in
really_input chan string 0 nbytes;
finalize()
string
with exn ->
(* finalize channel *)
finalize ();
(* re-raise exception *)
raise exn;;
也就是:
let unwind_protect body finalize =
try
let res = body() in
finalize();
res
with exn ->
finalize();
raise exn;;
let read file =
let chan = open_in file in
unwind_protect
(fun () ->
let nbytes = in_channel_length chan in
let string = Bytes.create nbytes in
really_input chan string 0 nbytes;
string)
(fun () -> close_in chan);;
Reference Cell
如上,
:= : int -> ref int -> unit
! : ref int -> int
for ... do ... done; : unit
如果没有 ref,那么,in
后面就是一个函数,不可能对 result 造成影响。
而有了 ref,那么,result 就成了一个“内存地址”,中间的函数就相当于“传引用”,因此 result 指向的地址的值可以被改变。