文章摘要
本文介绍了Racket语言(Lisp方言)的核心特性:代码即数据、括号作为纯结构、程序可编写程序。回顾了Lisp自1958年诞生以来的历史,强调其开创了垃圾回收、一等函数、REPL、条件表达式和同像性等现代编程概念。
文章总结
Racket友好入门指南
2026年8月11日
"学习Lisp的价值在于,当你最终理解它时,会获得深刻的启迪体验。"——埃里克·S·雷蒙德
欢迎。今天你将学习编程史上最古老、最独特的语言家族之一。这是一种代码即数据、括号即纯粹结构、程序可以编写程序的语言。本教程结束时,你将编写出自己的语法。
一点历史
Lisp诞生于1958年,由约翰·麦卡锡在麻省理工学院发明。它是仍在使用的第二古老的高级语言(仅比1957年的Fortran晚一年)。Python诞生于1991年,JavaScript在1995年。Lisp比它们早了30多年,许多我们现在认为"现代"的概念都诞生于此:
- 垃圾回收——为Lisp发明
- 一等函数——将函数作为参数传递,如今已成为标准
- REPL——交互式读取-求值-打印循环,Python、Node和Julia都拥有,始于Lisp
- 作为表达式的条件语句——返回值的if
- 同像性——代码本身就是语言的数据结构。这是最重要的概念,我们最后会回到这一点。
几十年来,Lisp一直是人工智能的语言。70年代和80年代,有专门为直接运行Lisp而设计的物理计算机:Symbolics和LMI制造的Lisp机器。随后"AI寒冬"来临,资金枯竭,Lisp从明星语言变成了小众语言。
但有趣的想法不会消亡——它们会变异,这正是Lisp之美的一部分。
从Lisp到Scheme再到Racket
1975年,杰拉德·萨斯曼和盖伊·斯蒂尔创建了Scheme:一种极简、优雅、近乎数学化的Lisp。Scheme成为学术界教授编程的首选语言(传奇著作《计算机程序的构造和解释》就是用Scheme编写的)。
1995年,马蒂亚斯·费莱森团队创建了PLT Scheme,这是一种专为教育和编程语言研究设计的Scheme。2010年更名为Racket,如今它已远不止是Scheme:它是一种用于构建语言的语言。其非官方座右铭是"语言导向编程":如果你的问题需要自己的语言,Racket让你一个下午就能构建一个。
| 年份 | 事件 | |------|------| | 1958 | 麦卡锡在麻省理工学院发明Lisp | | 1975 | 萨斯曼和斯蒂尔创建Scheme | | 1984 | Common Lisp标准化 | | 1995 | PLT Scheme(现Racket)诞生 | | 2007 | Clojure诞生(JVM上的Lisp) | | 2010 | PLT Scheme更名为Racket | | 今天 | 你,正在阅读本文,即将编写括号 |
今天谁在使用Lisp?
比你想象的更多人:
- Clojure 在银行、航空公司和初创公司的生产环境中运行(拉丁美洲最大的数字银行Nubank运行在Clojure上)
- Common Lisp(使用SBCL编译器)仍然活跃在专家系统、航班规划(ITA Software,被谷歌收购,为Google Flights提供支持)和科学计算领域
- Emacs Lisp——数百万人每天都在运行Lisp而不自知,因为他们的编辑器就是一个Lisp解释器
- Guile/Guix——整个Linux发行版完全用Scheme配置
- Racket 拥有自己的年度会议(RacketCon),活跃的学术和艺术社区,用于语言研究、形式验证(Rosette)、排版出版(Pollen)和全球教育
- 新的Lisp不断涌现:Fennel(编译为Lua的Lisp,在游戏开发中流行)、Janet、Hy(Python之上的Lisp)
安装(5分钟)
前往 https://racket-lang.org 下载适合你系统的安装程序(Linux、macOS、Windows)。打开随附的DrRacket环境。
DrRacket有两个区域:顶部编写定义(你的程序),底部有REPL用于实时实验。在定义区域的第一行,写入:
```
lang racket
```
这行告诉Racket你使用哪种语言(记住:Racket是一个语言工厂,所以你必须选择一种)。
如果你更喜欢终端:racket命令提供REPL,raco是包管理器和工具命令。
在REPL中尝试:
```
(+ 1 2) 3 (* 3 (+ 2 2)) 12 (string-append "hello " "world") "hello world" ```
Lisp的规则可以用一句话概括:
一切都是 (操作符 参数1 参数2 ...)。始终如此。没有例外。
没有需要记忆的运算符优先级,没有任何特殊语法。(+ 1 2)做加法,(if ...)做判断,(define ...)做命名。那些初看令人生畏的括号,实际上恰恰是任意规则的完全缺失。一周后,你就不会再注意到它们了。
定义和函数
```racket
lang racket
(define pi-approx 3.14159) (define (circle-area r) (* pi-approx r r)) (circle-area 2) ; => 12.56636 ```
define加名称创建常量define加(名称 参数...)创建函数- 注释以
;开头
匿名函数使用lambda(没错,就是1930年代丘奇λ演算中的那个lambda,它是这一切的理论祖先):
racket
(lambda (x) (* x x)) ; 一个没有名字的函数
((lambda (x) (* x x)) 5) ; => 25,直接应用
列表:Lisp的核心
Lisp代表"列表处理"。列表是基本结构:
racket
(list 1 2 3) ; => '(1 2 3)
'(1 2 3) ; 同上,"引号"
(first '(1 2 3)) ; => 1
(rest '(1 2 3)) ; => '(2 3)
(cons 0 '(1 2 3)) ; => '(0 1 2 3)
(length '(a b c)) ; => 3
注意引号'。它告诉Racket:不要求值,这是数据。记住这个细节——它是通往最终技巧的大门。
高阶函数
这是Racket的闪光点。将函数传递给其他函数是最自然不过的事:
racket
(map (lambda (x) (* x x)) '(1 2 3 4 5)) ; => '(1 4 9 16 25)
(filter even? '(1 2 3 4 5 6)) ; => '(2 4 6)
(foldl + 0 '(1 2 3 4 5)) ; => 15
map转换,filter筛选,foldl累积。有了这三个函数,你可以在不写任何for循环的情况下解决大多数列表问题。
递归:螺旋式思考
在Lisp中,你不思考"重复N次",而是思考"基本情况是什么,以及如何向它靠近":
racket
(define (factorial n)
(if (= n 0)
1
(* n (factorial (- n 1)))))
(factorial 5) ; => 120
为了让事情更直观,我们来画点东西。Racket内置了图形库:
```racket
lang racket
(require 2htdp/image) (define (sierpinski level) (if (= level 0) (triangle 8 "solid" "purple") (let ([t (sierpinski (- level 1))]) (above t (beside t t))))) (sierpinski 6) ```
将其粘贴到DrRacket中,点击运行,观察谢尔宾斯基三角形出现在屏幕上。
压轴大戏:编写代码的代码
还记得引号'吗:它将代码变成数据。看:
racket
'(+ 1 2) ; => 列表(+ 1 2),而不是数字3
(first '(+ 1 2)) ; => 符号+
(eval '(+ 1 2)) ; => 3。你刚刚将数据作为代码求值了
你的程序是一个列表。你可以构建列表。因此:你可以用程序构建程序。这就是同像性,也是为什么Lisp拥有真正的宏——不是C语言中的文本宏,而是在任何代码运行之前,接收代码并返回代码的函数。
Racket没有while循环?让我们发明一个:
racket
(define-syntax-rule (while condition body ...)
(let loop ()
(when condition
body ...
(loop))))
(define counter 0)
(while (< counter 5)
(displayln counter)
(set! counter (+ counter 1)))
你刚刚扩展了语言!在Lisp中,语法是你的。
艾伦·凯称Lisp为"软件的麦克斯韦方程组":一个微小的核心,从中可以推导出一切。
下一步是什么?
评论总结
根据评论内容,总结如下:
主要观点与论据:
对Racket/Lisp的兴趣与尝试意愿(评论1、4、6)
- 用户对Racket应用场景好奇,但发现多为库和开发工具。
- 有用户表示会尝试用Racket写Black Jack游戏。
- 关键引用:em-bee "any time the topic of racket comes up, i wonder if there are any interesting apps i could explore. but all i find is libraries and dev tools";mcbk2142 "I will try writing Black Jack in it!"
对教程友好性的批评(评论5)
- 认为该教程并非友好入门,而是“速通”,假设读者已了解lambda和语法规则。
- 关键引用:fn-mote "this is not a friendly intro. It is a speedrun... I don’t expect it to assume that I know what lambda is"
对语言实用性的质疑(评论7、10)
- 认为Racket/Lisp在实际中很少被使用,可能因部署不便。
- 有评论指出Lisp在AI领域早已被Prolog取代,美国因技术债务仍在使用。
- 关键引用:zerr "nobody is using it in the wild... cumbersome deployment options";ux266478 "Lisp had fallen from relevance before then... Prolog displaced it in the late 1970s"
对语言特性的正面评价(评论2、8)
- 提及Lisp的continuations机制用于错误恢复,以及宏和语言导向编程的灵活性。
- 关键引用:perrygeo "Lisp continuations allow for graceful error recovery";usxr1515 "Racket's approach to macros and language-oriented programming is one of the things I keep coming back to as a reference"
其他技术讨论(评论3、9、11)
- 有用户认为Scheme的唯一吸引力是热重载;有用户展示Racket的语法特性;有用户提及Janet语言。
平衡性总结: - 正面观点:对Racket/Lisp的兴趣、语言特性(宏、continuations)的认可。 - 负面观点:教程不友好、实际使用率低、部署困难、在AI领域已被Prolog取代。 - 中立观点:部分用户持观望或尝试态度,也有用户关注其他类似语言(如Janet)。