OCaml 函数式编程语言

精选 OCaml 常用指令与核心速查备忘单,涵盖高频用法、配置参数与实用技巧。 本 OCaml 备忘单为您提供 OCaml 函数式编程语言的核心语法与常用 API 快速检索手册。

#🚀 入门指引

#基础入门脚本 (hello.ml)

let () =
  let message = "Hello, World!" in
  Printf.printf "%s\n" message

编译与运行 (Compile and Run)

$ ocamlc -o hello.byte hello.ml
$ ./hello.byte

使用 Dune 构建与运行 (Build & Run with Dune)

$ dune build hello.exe
$ _build/default/hello.exe

# 也可以直接运行
$ dune exec ./hello.exe

详细信息请参阅 dune 官方文档。

#模块导入 (Imports)

使用 opam 安装外部扩展模块:

$ opam install hex

全局打开模块 (Global Open)

open Hex

局部打开模块 (Local Open)

Hex.of_string "hex string"

let open Hex in
  of_string "to hex"

#代码注释

单行与多行注释 (Line & Block Comments)

(* 单行注释 *)

(* 多行注释
* 当我们需要详细说明
* 某些复杂逻辑时 *)

(* 外层注释
   (* 支持嵌套注释 *)
   外层注释结束 *)

档注释 (Documentation Comments)

val sum : int -> int -> int
(** [sum x y] 返回两个整数
 [x] 和 [y] 的加和总值 *)

#数据类型

#预定义基础类型 (Predefined Types)

Unit 类型

注意:# 表示在交互式终端 (Toplevel) 中执行指令及返回结果

# ();; (* 相当于 C 语言中的 void *)
- : unit = ()

核心基础类型 (Basic Types)

# 5 ;; (* 整数 int *)
- : int = 5

# 3.14 ;;  (* 浮点数 float *)
- : float = 3.14

# true ;; (* 布尔值 bool *)
- : bool = true

# false ;;
- : bool = false

# 'a' ;; (* 字符 char *)
- : char = 'a'

# "a string" ;; (* 字符串 string *)
- : string = "a string"

# String.to_bytes "hello" ;; (* 字节数组 bytes *)
- : bytes = Bytes.of_string "hello"

# (3, 5);; (* 元组 tuple *)
- : int * int = (3, 5)

# ref 0;; (* 可变引用 reference *)
- : int ref = {contents = 0}

Options 与 Results 包装类型

# Some 42;;
- : int option = Some 42

# Ok 42;;
- : (int, 'a) result = Ok 42

# Error "404";;
- : ('a, int) result = Error 404

#数组与列表 (Arrays & Lists)

数组 (Arrays)

# [|0; 1; 2; 3|];; (* 创建数组 *)
- : int array = [|0; 1; 2; 3|]

# [|'u'; 's'; 'c'|].(1);; (* 访问数组元素 *)
- char = 's'

数组是可变的 (Mutable):

let scores = [|97; 85; 99|];;
- : int array = [|97; 85; 99|]

# scores.(2) <- 89;; (* 修改更新数组元素 *)
- unit = ()

# scores;;
- : int array = [|97; 85; 89|]

列表 (Lists)

# [1; 2; 3];;
- : int list = [1; 2; 3;]

# ["a"; "str"; "lst"];;
- : string list = ["a"; "str"; "lst"]

列表是不可变的 (Immutable):

# let lst = [1; 2; 3];;
- : int list = [1; 2; 3]

# let new_lst =  0 :: lst;; (* 头部追加元素生成新列表 *)
- : int list = [0; 1; 2; 3]

# new_lst @ [4;5;6];; (* 拼接合并两个列表 *)
- : int list = [0; 1; 2; 3; 4; 5; 6]

#自定义类型 (User-Defined Types)

记录体 (Records)

用于将相关关联的数据打包声明:

type person = {
  name: string;
  age: int
}

# let zeno = {name = "Zeno"; age = 30};;
val zeno : person = {name = "Zeno"; age = 30}

变体类型 (Variants)

定义多个不同但相互关联的类型分支:

type shape =
  | Circle of float
  | Rectangle of float * float

# let my_shape = Circle 5.0;;
- : shape = Circle 5.

类型别名 (Aliases)

为复杂或高频使用的类型赋予具名含义:

type point = float * float

# let origin: point = (0.0, 0.0);;
val origin : point = (0., 0.)

#函数 (Functions)

#函数定义 (Functions)

单参数函数

let add_one x =
  let result = x + 1 in
  result

# add_one 1;;
- : int = 2

多参数函数

let sum x y =
  let result = x + y in
  result

# sum 1 2;;
- : int = 3

元组参数函数

let str_concat (x, y) =
  x ^ " " ^ y

# str_concat ("Hello", "OCaml") ;;
- : string = "Hello Ocaml"

#递归函数 (递归处理 Functions)

rec 关键字

所有递归调用的函数均必须显式添加 rec 关键字:

let rec factorial n =
  if n < 1 then 1 else n * factorial (n - 1)

上述普通递归在深层调用时可能引发栈溢出 (Stack overflow)。

尾递归优化 (Tail Recursion)

借由辅助累加器参数实现高效尾递归:

let rec factorial_helper n acc =
  if n = 0 then acc
  else factorial_helper (n - 1) (n * acc)

注意最后一次表达式调用即为递归函数自身:

let factorial n = factorial_helper n 1

#链式调用 (Chaining)

应用运算符 (Application Operator)

由右向左读取求值,优先计算 sum 2 3

(* 计算 log(2 + 3) *)
# log @@ float_of_int @@ sum 2 3 ;;
- : float = 1.609...

管道运算符 (Pipeline)

(* 计算 log((x + y)!) *)
# sum 2 3
  |> factorial
  |> float_of_int
  |> log ;;
- : float = 4.787...

|> 将前一个函数的返回值作为输入传给管道中的下一个函数

#流程控制 (Control Flow)

#条件判断 (If Statements)

If 表达式

let is_pos x =
  if x > 0 then "positive" else "negative"

If else if 多分支

let f x =
  if x > 3 then "gt 3"
  else if x < 3 then "lt 3"
  else "eq 3"

模式匹配 (Pattern Matching)

let is_pos x =
  match x > 0 with
  | true  -> "positive"
  | false -> "negative"

#循环结构 (Loops)

For 循环

for i = 1 to 5 do
  print_int i
done

While 循环

注意需要借助 ref 可变引用使条件最终变为 false 结束循环:

let i = ref 0 in
  while !i < 5 do
    print_int !i;
    i := !i + 1
  done

#运算符 (Operators)

比较运算符

=         (* 等于 *)
<>        (* 不等于 *)
>         (* 大于 *)
<         (* 小于 *)
>=        (* 大于等于 *)
<=        (* 小于等于 *)

算术运算符

(* 整数运算符       浮点数运算符 *)
+                 +.  (* 加法 *)
-                 -.  (* 减法 *)
*                 *.  (* 乘法 *)
/                 /.  (* 除法 *)
                  **  (* 求幂 *)

#实用工具模块 (Useful Tools)

#List 列表处理 (List)

查找与过滤 (Searching & Filtering)

# let lst = [1; 2; 3];;
val lst : int list = [1; 2; 3]

# List.filter (fun x -> x mod 2 = 0) lst;;
- : int list = [2]

# List.find (fun x -> x = 4) lst;;
Exception: Not_found

# List.sort compare [2; 1; 3];;
- : int list = [1; 2; 3]

转换与映射 (Applying Transformations)

(* 遍历列表并对每项执行函数 f *)
List.iter f lst

(* 将映射函数作用于每个元素 *)
(* 例:将列表中的元素翻倍 *)
List.map (fun x -> x + x) lst

(* 在元素之间应用运算符归约 *)
(* 例:1 + 2 + 3 *)
List.fold_left (+) 0 lst

#关联列表 (Association Lists)

定义与访问

let scores =
  [("math", 91); ("phil", 89); ("stats", 94)]

# List.assoc "stats" scores;;
- : int = 94

# List.mem_assoc "math" scores;;
- : bool = true

拆分与合并

# List.split scores;;
- : string list * int list = (["math"; "phil"; "stats"], [91; 89; 94])

# List.combine [1;2;3] [4; 5; 6];;
- : (int * int) list = [(1, 4); (2, 5); (3, 6)]

关联列表类似字典或 Hashmap 哈希表。

#哈希表 (Hash Tables)

哈希表是可变的 (Mutable)。

初始化与添加数据


# let my_htable = Hashtbl.create 3;;
val my_htable : ('_weak1, '_weak2) Hashtbl.t = <abstr>

# Hashtbl.add my_htable "A" "John";
  Hashtbl.add my_htable "A" "Jane";
  Hashtbl.add my_htable "B" "Max";;

数据查找

# Hashtbl.find my_htable "A";;
- : string = "Jane"

(* 查找所有匹配项 *)
# Hashtbl.find_all my_htable "A";;
- : string list = ["Jane"; "John"]

#映射表 (Maps)

Map 是不可变的键值对关联表。

初始化与添加数据

(* 使用 Map.Make 函子创建自定义 map 模块 *)
# module StringMap = Map.Make(String);;

let books =
  StringMap.empty
  |> StringMap.add "Dune" ("Herbet", 1965)
  |> StringMap.add "Neuromancer" ("Gibson", 1984)

查找数据

(* find_opt 返回包裹在 option 中的值,不存在返回 None *)
# StringMap.find_opt "Dune" books;;
- : (string * int) option = Some ("Herbet", 1965)

(* find 直接返回关联值,不存在抛出 Not_Found *)
# StringMap.find "Dune" books;;
- : string * int = ("Herbet", 1965)

插入与移除数据

由于 Map 不可变,操作会返回全新的 Map 对象:

let more_books = books
  |> StringMap.add "Foundation" ("Isaac Asimov", 1951)

let less_books =
  |> StringMap.remove "Dune"

过滤筛选:

let eighties_books =
    StringMap.filter
      (fun _ (_, year) -> year > 1980 & number < 1990) books

遍历打印数据

let print_books map =
  StringMap.iter (fun title (author, year) ->
    Printf.printf "Title: %s, Author: %s, Year: %d\n" title author year
  ) map

# let () = print_books eighties_books;;
Title: Neuromancer, Author: Gibson, Year: 1984