1 欢迎来到 Racket
根据视角的不同,Racket 可以是:
一种编程语言—
一种 Lisp 的方言和 Scheme 的后裔; 更多关于 Lisp 方言以及它们与 Racket 之间关系的信息, 见Dialects of Racket and Scheme。
一个编程语言家族—
包括 Racket 的多种变体,还有更多; 一组工具—
用于该编程语言家族。
在没有歧义时,我们就简单地使用 Racket。
Racket 的主要工具包括:
racket,核心编译器、解释器和运行时系统;
DrRacket,编程语言环境;
raco,用于执行 Racket 命令(Racket commands) 的命令行工具,包括安装包,构建库等等。
通常,你可以用 DrRacket 来探索 Racket 语言,特别是在开始阶段。 根据自己偏好,你也可以按照自己的偏好使用命令行 racket 解释器和你喜欢的文本编辑器,详情请参阅Command-Line Tools and Your Editor of Choice。 本文档中其余介绍语言的部分大都与你选择的编辑器无关。
如果你使用 DrRacket,就需要选择适当的语言,因为 DrRacket 支持多种 Racket 变体和其它语言。如果你从未使用过 DrRacket,请启动它,并在 DrRacket 上方的文本区内输入
之后点击文本区上方的运行按钮,这样 DrRacket 就明白你要使用 Racket 的正常变体了(这里的“正常”是相对于更小的 racket/base 或其它可用的变体而言的)。
More Rackets一节中描述了其它可用的变体。
如果你曾经在 DrRacket 中编写过不以 #lang 开头的程序,那么 DrRacket 会记住你上一次使用的语言,而非根据 #lang 来推断。此时,请点击 语言|选择语言...
1.1 与 Racket 进行交互
DrRacket 下方的文本区和 racket 命令行程序(在无选项启动时) 都可以视作一种计算器。你只需输入 Racket 表达式,敲下回车键,答案就打印出来了。 按照 Racket 的术语,这种计算器叫做读取-求值-打印 循环 (read-eval-print loop),简称 REPL。
数字本身就是就是表达式,其答案就是该数字。
> 5 5
字符串也是求值为其自身的表达式。字符串写在两个双引号之间:
> "Hello, world!" "Hello, world!"
Racket 使用圆括号来括住较大的表达式—
> (substring "the boy out of the country" 4 7) "boy"
1.2 定义与交互
你可以通过 define 形式来定义自己的函数,让它像 substring 那样工作:
(define (extract str) (substring str 4 7))
> (extract "the boy out of the country") "boy"
> (extract "the country out of the boy") "cou"
尽管你可以在 REPL 中对 define 形式进行求值, 然而定义通常是程序的一部分,你可能想要保留它以备后用。因此在 DrRacket 中,通常你应将定义连同 #lang 前缀一起放在上方的文本区, 即定义区内:
#lang racket (define (extract str) (substring str 4 7))
如果调用 (extract "the boy") 是你的程序主要活动的一部分, 那么它也应当放在定义区内。但如果它只是你用来测试 extract 的示例表达式,那么你可能更愿意将定义区保持为如上状态,点击运行, 然后在 REPL 中对 (extract "the boy") 求值。
在使用命令行 racket 而非 DrRacket 时,你应当用编辑器将以上文本保存在文件中。 如果你将它保存为 "extract.rkt",那么在同一目录下执行 racket 之后,就会对以下序列求值:
如果你使用 xrepl,那么可以用 ,enter extract.rkt.
> (enter! "extract.rkt") > (extract "the gal out of the city") "gal"
enter! 形式会加载该代码并将求值上下文切换到该模块中,就像 DrRacket 的运行按钮那样。
1.3 创建可执行文件
如果你的文件(或 DrRacket 的 定义区)中包含
#lang racket (define (extract str) (substring str 4 7)) (extract "the cat out of the bag")
那么它就是个在运行时会打印“cat”的完整程序。你可以使用 DrRacket 或在 racket 中使用 racket 来运行该程序。但如果该程序保存在 ‹src-filename› 中,你也可以在命令行中运行它:
racket ‹src-filename›
你有几种可选的方式将程序打包成可执行文,:
在 DrRacket 中,你可以选择 Racket|创建可执行程序... 菜单项。
在命令行中,请执行 raco exe ‹src-filename›,其中 ‹src-filename› 包含该程序。更多信息见 raco exe: Creating Stand-Alone Executables。
对于 Unix 或 Mac OS,你可以在该文件的最开始处插入
更多关于脚本文件的信息见 Scripts。
#! /usr/bin/env racket
来将其转换为可执行脚本。此外,你还需要在命令行中用 chmod +x ‹filename› 为其赋予可执行权限。
只要 racket 在用户的可执行文件搜索路径(即 PATH 环境变量)中,该脚本就能工作。如果在 #! 之后使用 racket 的完整路径(#! 和路径之间留有一空格), 那么用户的可执行文件搜索路径就无关紧要了。
1.4 Lisp/Scheme 经验者注意
如果你对 Scheme 或 Lisp 有所了解,那么你可能会试图将
(define (extract str) (substring str 4 7))
放在 "extract.rktl" 中并以如下方式运行 racket
> (load "extract.rktl") > (extract "the dog out") "dog"
这样确实可以工作,因为 racket 会模拟出传统的 Lisp 环境。 不过我们强烈建议你不要在模块之外使用 load 或编写程序。
在模块外编写程序会结合糟糕的错误消息、低劣性能和笨拙的脚本来运行程序。 这些问题并非针对于 racket,它们是传统上层环境的根本限制, 历史上 Scheme 和 Lisp 的实现与造成这些限制的特设(ad-hoc)命令行参数、 编译器指令和构建工具作过斗争。模块系统的设计就是用来避免这些问题的, 因此从长远来看,以 #lang 开始会让 Racket 的使用体验更加愉快。