1 Welcome to Racket
从不同角度看,Racket 是
一种 programming language——Lisp 的一种方言和 Scheme 的后裔;
关于 Lisp 的其他方言及其与 Racket 的关系,详见 Dialects of Racket and Scheme。
一组编程family——Racket 的变体,或更广泛家族;或
一组 tools——用于使用一组编程family的工具集。
在没有歧义的情况下,我们简称为 Racket。
Racket 的主要工具有
racket, the core compiler, interpreter, and run-time system;
DrRacket, the programming environment; and
raco, a command-line tool for executing Racket commands that install packages, build libraries, and more.
最有可能的是,你会想用 DrRacket 来探索 Racket 语言, 特别是在刚开始的时候。如果你愿意,也可以使用命令行 racket 解释器(见 Running racket and gracket)和你最喜欢的文本编辑器 (见 命令行工具与你选择的编辑器)。本指南的其余部分在介绍语言时 与你选择的编辑器基本无关。
如果你使用 DrRacket,你需要选择合适的语言,因为 DrRacket 支持多种不同的 Racket 变体以及其他语言。假设你 之前从未使用过 DrRacket,启动它,在 DrRacket 的顶部文本区域 输入以下行
#lang racket
然后点击文本区域上方的 Run 按钮。 DrRacket 就会理解你打算使用标准 Racket 变体(而不是更小的 racket/base 或许多其他可能性)。
More Rackets 介绍了其他一些可能性。
如果你之前使用 DrRacket 时没有使用以 #lang 开头的程序, DrRacket 会记住你上次使用的语言,而不是从 #lang 行推断语言。 在这种情况下,使用 Language|Choose Language... 菜单项。 在出现的对话框中,选择第一项,它告诉 DrRacket 使用通过 #lang 在源程序中声明的语言。仍然将 #lang 行放在 顶部文本区域中。
1.1 Interacting with Racket
DrRacket 的底部文本区域和 racket 命令行程序(不带选项启动时) 都充当一种计算器。你输入一个 Racket 表达式,按 Return 键,答案就会 被打印出来。用 Racket 的术语来说,这种计算器被称为 读取-求值-打印循环或 REPL。
一个数字本身就是一个表达式,答案就是该数字:
> 5 5
字符串也是一种求值为自身的表达式。字符串用双引号 写在开头和结尾:
> "Hello, world!" "Hello, world!"
Racket 使用括号来包裹较大的表达式——除了简单常量之外的几乎任何 类型的表达式。例如,函数调用写作:左括号、函数名、参数表达式 和右括号。以下表达式调用内置函数 substring,参数为 "the boy out of the country"、4 和 7:
> (substring "the boy out of the country" 4 7) "boy"
1.2 Definitions and Interactions
你可以通过 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 的一个示例表达式,那么你更可能保留上述的 定义区,点击 Run,然后在 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 的 Run 按钮一样。
1.3 Creating Executables
如果你的文件(或 DrRacket 中的 定义区)包含
#lang racket (define (extract str) (substring str 4 7)) (extract "the cat out of the bag")
那么它就是一个完整的程序,运行时打印 “cat”。你可以在 DrRacket 中运行该程序,也可以在 racket 中使用 enter! 运行,但如果程序保存在 ‹src-filename› 中, 你还可以通过命令行运行它:
racket ‹src-filename›
要将程序打包为可执行文件,你有以下几个选项:
在 DrRacket 中,你可以选择 Racket|Create Executable... 菜单项。
在命令行提示符下,运行 raco exe ‹src-filename›,其中 ‹src-filename› 包含 该程序。更多信息见 (part ("(lib scribblings/raco/raco.scrbl)" "exe"))。
在 Unix 或 Mac OS 上,你可以通过在文件最开头插入以下行 将程序文件转换为可执行脚本:
关于脚本文件的更多信息见 Scripts。
#! /usr/bin/env racket
同时,在命令行上使用 chmod +x ‹filename› 将文件权限改为可执行。
只要 racket 在用户的可执行搜索路径中,脚本就能工作。 或者,可以在 #! 之后使用 racket 的完整路径 (在 #! 和路径之间留一个空格),这样用户的 可执行搜索路径就不重要了。
1.4 A Note to Readers with Lisp/Scheme Experience
如果你已经了解一些 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 实现历史上一直通过临时的命令行标志、编译器指令和 构建工具来应对。模块系统旨在避免这些问题,所以从 #lang 开始,长期来看你会对 Racket 更加满意。