可能存在事实性错误,暂时没有排查错误,一口气写完的
我们即将开展学习的这门语言:OCaml,是一种函数式编程语言,但它不仅仅支持函数式编程,还支持多范式编程,涵盖了命令式编程、面向对象编程和并发编程等多种范式。OCaml 作为 ML(Meta Language)家族的一员,继承了 ML 语言的函数式编程特性,同时也添加了对其他编程风格的支持。
CS61A 中也有这个概念,使用的是 Scheme,最后的 Project 是在 Python 中构建 Scheme 的 Interpreter,《Fluent Python 2nd》 有简单实现一个类似的解释器,参照 lispy
函数式编程作为编程语言的一种范式流传至今,从 Lisp 这位元祖开始,发展出许多编程语言,如 Scheme, Racket, OCaml, Haskell, Moonbit... 整个社区时常产生类似 Vim 还是 Emacs 好的争论。是纯函数式好还是多范式编程更好?这类问题放在不同人上只会有无意义的争锋相对,各执己见,咱学完也别淌这趟浑水,打个太极☯️,喜欢啥就用啥。学完这些语言,你也很难在国内找到一份“语言对口”的工作。很多国际大厂在内部都有在使用这类语言实现项目。Jane Street 几乎纯使用 OCaml 构建了整个公司的系统框架,也为整个 OCaml 社区做出极大贡献。
画风切换回来,什么是 Meta Language?早期人们希望通过一种共同语言来描述、定义其他语言的语法结构与行为,以我的理解,这时的元语言是想提供一种创造或使用其他语言的框架。我们在学习 CLRS 碰到的算法伪代码,并不直接用于编写最终的代码,而是作为定义、分析整体算法的工具为我们使用。元语言本身并不直接用于编写最终的应用程序,而是作为定义、分析、编译、解释或处理其他编程语言的工具。随着时间的推移,ML 发展成了一种功能强大的编程语言,具有类型推导、模式匹配和强类型系统等特性。
从现实看,函数式编程的教育在国内是极大忽视的,而这门课在国外几乎是“必需品”。函数式编程的理念也深深嵌在现代流行的编程语言中。至于为什么选择 OCaml 作为本轮学习的主要编程语言,纯属我个人强买强卖(其实是好的资源比较多,图标也很好看👍)
我个人很中意 Pattern Matching 这个概念,举个例子
在 OCaml 中,
(* 定义几何形状类型 *)
type shape =
| Circle of float (* 圆形,包含半径 *)
| Rectangle of float * float (* 矩形,包含宽和高 *)
| Triangle of float * float * float (* 三角形,包含三边 *)
(* 计算面积的函数 *)
let area s =
match s with
| Circle r -> 3.1415 *. r *. r (* 圆形的面积公式:πr² *)
| Rectangle (w, h) -> w *. h (* 矩形的面积公式:宽 × 高 *)
| Triangle (a, b, c) ->
(* 海伦公式:sqrt(p * (p - a) * (p - b) * (p - c)),其中 p 是半周长 *)
let p = (a +. b +. c) /. 2.0 in
sqrt (p *. (p -. a) *. (p -. b) *. (p -. c))
(* 测试 *)
let _ =
Printf.printf "Circle area: %f\n" (area (Circle 5.0));
Printf.printf "Rectangle area: %f\n" (area (Rectangle (3.0, 4.0)));
Printf.printf "Triangle area: %f\n" (area (Triangle (3.0, 4.0, 5.0)));
在 Python 中(好像没办法简单写)
import math
class Circle:
def __init__(self, radius):
self.radius = radius
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
class Triangle:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def calculate_area(shape):
match shape:
case Circle(r):
return math.pi * r * r
case Rectangle(w, h):
return w * h
case Triangle(a, b, c):
p = (a + b + c) / 2
return math.sqrt(p * (p - a) * (p - b) * (p - c))
circle = Circle(5.0)
rectangle = Rectangle(3.0, 4.0)
triangle = Triangle(3.0, 4.0, 5.0)
print(f"Circle area: {calculate_area(circle)}")
print(f"Rectangle area: {calculate_area(rectangle)}")
print(f"Triangle area: {calculate_area(triangle)}")
看上去也没啥好坏之分,确实如此,但我再摆出红黑树呢!魅力一下就显现了=》
在构建红黑树时,我们会遇到一些构造起来十分头疼的 case,转化到实际的代码编写又是一门苦差事,这里我摆出 balance 操作在 OCaml 的实现,读者可以在未来实现过红黑树后再来看看。
# 摘自 https://cs3110.github.io/textbook/chapters/ds/rb.html#balance
type color = Red | Black
type 'a rbtree = Leaf | Node of color * 'a * 'a rbtree * 'a rbtree
let balance = function
| Black, z, Node (Red, y, Node (Red, x, a, b), c), d
| Black, z, Node (Red, x, a, Node (Red, y, b, c)), d
| Black, x, a, Node (Red, z, Node (Red, y, b, c), d)
| Black, x, a, Node (Red, y, b, Node (Red, z, c, d)) ->
Node (Red, y, Node (Black, x, a, b), Node (Black, z, c, d))
| a, b, c, d -> Node (a, b, c, d)
OCaml 还支持其他编程范式,比如:
命令式编程:实现了引用、可变数据结构和控制流语句(如 for 和 while 循环)
面向对象编程:提供了类、对象、继承等基本特性,并且能够与函数式编程无缝结合。
并发编程:拥有多种并发编程的方式,支持轻量级线程和并发数据结构。
说太多历史也不大好,本篇的主线是指导环境配置的工作。
请参照:https://cs3110.github.io/textbook/chapters/preface/install.html
我们的学习也会围绕 CS3110 的教材展开,根据内容进行配置即可。
(这个师兄老是丢链接然后就不管了,好坏啊!)会有实际演示和答疑的,别担心!
本轮学习将与华师 24 级图灵班的学习同步(其实是飞速)开展,面向所有愿意共同学习的朋友,预期是在本学期中学习完 CS3110。本教材会同步学习一些算法与数据结构,与 CLRS 的学习并无冲突,前者偏向实践,另一则更希望读者掌握数学和分析算法的工具。
个人学习 OCaml 的经验尚浅,此次学习也是希望能够巩固自身,顺便将之前觉得重要的概念分享出来。在毕业之前最后抖几下🥲
俊坚师兄也分享了自己的学习经验,可移步至 CS-Plan 查看
有关形式化证明的学习,会参照 Software Foundation 学习,日后再议!
我会尽量将视频上传至论坛 B 站,本贴也会跟踪相关信息。开学后,我会首先填坑,带着各位一起看看如何用 OCaml 实现一种语言的编译器。