Chez Scheme 程序设计语言(第4版本)
- 简介(Introduction)
Scheme is a general-purpose computer programming language. It is a high-level language, supporting operations on structured data such as strings, lists, and vectors, as well as operations on more traditional data such as numbers and characters. While Scheme is often identified with symbolic applications, its rich set of data types and flexible control structures make it a truly versatile language. Scheme has been employed to write text editors, optimizing compilers, operating systems, graphics packages, expert systems, numerical applications, financial analysis packages, virtual reality systems, and practically every other type of application imaginable. Scheme is a fairly simple language to learn, since it is based on a handful of syntactic forms and semantic concepts and since the interactive nature of most implementations encourages experimentation. Scheme is a challenging language to understand fully, however; developing the ability to use its full potential requires careful study and practice.
Scheme 是一种通用计算机程序语言。Scheme是一种高级语言,支持结构化数据如字符串、列表和向量以及更多传统数据类型如数和字符等之上的操作。Scheme通常被认为是符号式的,它拥有丰富的数据类型和灵活的控制结构使得它是一个真正的万能型语言。Scheme已经被用于编写文本编辑器、优化编译器、操作系统、图形软件包、专家系统、数值应用程序、金融分析软件包、虚拟现实系统,以及你所能想象的所有其他类型应用。然而,Scheme语言也确实是一种简单易学的编程语言,这是由于Scheme是基于许多语法形式(syntacitc forms)和语义概念,此外也由于大多数Scheme实现的交互本质能促进程序员进行程序设计实验。尽管如此,完全理解Scheme语言是极具挑战的;要想完全发挥Scheme语言的所有潜力要求程序员进行仔细的研究和练习。
Scheme programs are highly portable across versions of the same Scheme implementation on different machines, because machine dependencies are almost completely hidden from the programmer. They are also portable across different implementations because of the efforts of a group of Scheme language designers who have published a series of reports, the "Revised Reports" on Scheme. The most recent, the "Revised6 Report" [24], emphasizes portability through a set of standard libraries and a standard mechanism for defining new portable libraries and top-level programs.
Scheme程序具有高度的可移植性(在不同机器上相同的Scheme实现),这是由于对于程序员,其机器依赖性几乎完全被隐藏了起来。在不同的Scheme实现上,Scheme程序也几乎拥有可一致性,这是因为一群Scheme语言设计者发布了一系列的报告,即Revised Reports on Scheme(RnRS)。其中最近的,是第6版(R6RS)[24],其中强调了通过一组标准库、定义新的可移植性库的机制以及顶层程序的方式来确保可移植性。
Although some early Scheme systems were inefficient and slow, many newer compiler-based implementations are fast, with programs running on par with equivalent programs written in lower-level languages. The relative inefficiency that sometimes remains results from run-time checks that support generic arithmetic and help programmers detect and correct various common programming errors. These checks may be disabled in many implementations.
尽管某些早期的Scheme系统确实既低效又缓慢,但是许多新的基于编译器的实现则是快速的,可以与那些使用较低级别的语言编写的程序相当。(Scheme的实现)运行期检测(run-time checks)可以支持通用算法和帮助程序员检查以及修正多种常见程序错误,而这种运行期检测有时会导致相对的低效。这些检测在许多Scheme实现种可以被关闭。
Scheme supports many types of data values, or objects, including characters, strings, symbols, lists or vectors of objects, and a full set of numeric data types, including complex, real, and arbitrary-precision rational numbers.
Scheme支持多种数据类型,对象,包括字符、字符串、符号、对象的列表或向量,以及许多数值数据类型,包括复数、实数以及精确有理数等。
The storage required to hold the contents of an object is dynamically allocated as necessary and retained until no longer needed, then automatically deallocated, typically by a garbage collector that periodically recovers the storage used by inaccessible objects. Simple atomic values, such as small integers, characters, booleans, and the empty list, are typically represented as immediate values and thus incur no allocation or deallocation overhead.
在Scheme中,对象所需的内存是按需动态分配的,并且会在不再需要时被自动释放,该特性主要通过一个垃圾收集器(garbage collector)周期性的恢复那些不可用对象的存储空间来实现。简单原子值,类似小整数、字符、布尔值以及空列表则以立即值的方式表示,不需要额外的分配和释放开销。
Regardless of representation, all objects are first-class data values; because they are retained indefinitely, they may be passed freely as arguments to procedures, returned as values from procedures, and combined to form new objects. This is in contrast with many other languages where composite data values such as arrays are either statically allocated and never deallocated, allocated on entry to a block of code and unconditionally deallocated on exit from the block, or explicitly allocated and deallocated by the programmer.
所有对象都是第一类值,与其表示无关;因为它们无法确定什么时候被释放,对象可以被自由地当作参数传递给过程,也可以作为过程的返回值,以及用来构造新的对象。作为对比,许多其他语言在处理组合数据值(比如数组)会静态分配一部分内存,并且不去释放它们,要么在进入代码块时分配,退出代码块时无条件释放,要么需要程序员显式地申请和释放内存。
Scheme is a call-by-value language, but for at least mutable objects (objects that can be modified), the values are pointers to the actual storage. These pointers remain behind the scenes, however, and programmers need not be conscious of them except to understand that the storage for an object is not copied when an object is passed to or returned from a procedure.
Scheme是一种按值调用的语言,但是对于可变对象(mutable objects - 可以被修改的对象),它们的值就是实际存储的指针。这些指针处于场景之后,尽管如此,程序员不需要意识到它们,除非有需要理解这个对象的存储在传参和返回值的时候并没有被拷贝。
At the heart of the Scheme language is a small core of syntactic forms from which all other forms are built. These core forms, a set of extended syntactic forms derived from them, and a set of primitive procedures make up the full Scheme language. An interpreter or compiler for Scheme can be quite small and potentially fast and highly reliable. The extended syntactic forms and many primitive procedures can be defined in Scheme itself, simplifying the implementation and increasing reliability.
Scheme语言的心脏是一个关于语法形式(syntactic forms)的小核心(core),通过这个核心可以构建其他所有的语法形式。这些核心形式,以及一组从它推导出的语法形式的集合和一组基本过程构成了完整的Scheme语言。一个Scheme的解释器或者编译器可以非常小,同时可能很快以及高度可靠。这些扩展形式和很多基本过程可以用Scheme自身定义出来,从而简化了整个Scheme的实现和其可靠性。
Scheme programs share a common printed representation with Scheme data structures. As a result, any Scheme program has a natural and obvious internal representation as a Scheme object. For example, variables and syntactic keywords correspond to symbols, while structured syntactic forms correspond to lists. This representation is the basis for the syntactic extension facilities provided by Scheme for the definition of new syntactic forms in terms of existing syntactic forms and procedures. It also facilitates the implementation of interpreters, compilers, and other program transformation tools for Scheme directly in Scheme, as well as program transformation tools for other languages in Scheme.
Scheme程序共用了关于Scheme数据结构的通用打印表示。这使得任何的Scheme程序本身也可以作为Scheme对象,具有自然和显而易见的内部表示。例如,当结构化的语法形式与列表关联(用列表形式表达)的时候,变量和语法关键词就和符号关联了起来。使用已经存在的语法形式和过程来定义新的语法形式,是Scheme所提供的语法扩展工具的基础。在Scheme中也可以因此而直接使用Scheme的解释器、编译器以及其他程序变形工具,而一些其他语言的变形器也是一样。
Scheme variables and keywords are lexically scoped, and Scheme programs are block-structured. Identifiers may be imported into a program or library or bound locally within a given block of code such as a library, program, or procedure body. A local binding is visible only lexically, i.e., within the program text that makes up the particular block of code. An occurrence of an identifier of the same name outside this block refers to a different binding; if no binding for the identifier exists outside the block, then the reference is invalid. Blocks may be nested, and a binding in one block may shadow a binding for an identifier of the same name in a surrounding block. The scope of a binding is the block in which the bound identifier is visible minus any portions of the block in which the identifier is shadowed. Block structure and lexical scoping help create programs that are modular, easy to read, easy to maintain, and reliable. Efficient code for lexical scoping is possible because a compiler can determine before program evaluation the scope of all bindings and the binding to which each identifier reference resolves. This does not mean, of course, that a compiler can determine the values of all variables, since the actual values are not computed in most cases until the program executes.
Scheme变量和关键词都是基于词法作用域的,并且Scheme程序是块结构。标识符可以被导入到一个程序或者库,(标识符)也可以被局部绑定在一个给定的代码块(如程序、库或者过程体)中。一个局部绑定仅仅在其可见词法域可见,也就是说仅在组成这段特定的代码块的程序文本中可见。出现在某标识符所在代码块以外的同名符号对应不同的绑定;若块外的标识符没有绑定,那么引用无效。代码块可以嵌套,且内部的代码块中的绑定可以屏蔽掉外部的同名绑定。一个绑定的作用域等于其标识符的可见范围扣除其被同名绑定屏蔽的区域。块结构与词法作用域有助于创建模块式的程序,且易于阅读和维护,也更可靠。针对词法作用域的高效代码生成是可能的,因为编译器可以在程序求值之前确定所有将被解析的引用所在的的绑定的作用域。当然,这并非是说编译器可以确定所有变量的值,因为在绝大多数情况下在执行程序前,变量的实际值还没有被计算出来。
In most languages, a procedure definition is simply the association of a name with a block of code. Certain variables local to the block are the parameters of the procedure. In some languages, a procedure definition may appear within another block or procedure so long as the procedure is invoked only during execution of the enclosing block. In others, procedures can be defined only at top level. In Scheme, a procedure definition may appear within another block or procedure, and the procedure may be invoked at any time thereafter, even if the enclosing block has completed its execution. To support lexical scoping, a procedure carries the lexical context (environment) along with its code.
在多数语言中,一个过程定义是一个名字与代码块的简单联系。块中的某些局部变量是过程的参数。在一些语言中,一个过程定义可以出在其他代码块或过程中,因此只有在执行到这个内部块时这个过程才被触发。另一些语言,过程仅能被定义在最顶层。在Scheme中,一个过程定义可以出现在其他代码块或过程中,在此之后该过程都可能被触发,即使它所在的代码块已经结束执行。为了支持词法作用域,一个过程会连同它的代码并携带它的词法上下文(环境)。
Furthermore, Scheme procedures are not always named. Instead, procedures are first-class data objects like strings or numbers, and variables are bound to procedures in the same way they are bound to other objects.
此外,Scheme的过程并非总被命名。与此不同,过程是第一类数据对象,就像字符串或数那样,而且那些变量绑定到过程的方式与绑定到其他对象的方式一样。
As with procedures in most other languages, Scheme procedures may be recursive. That is, any procedure may invoke itself directly or indirectly. Many algorithms are most elegantly or efficiently specified recursively. A special case of recursion, called tail recursion, is used to express iteration, or looping. A tail call occurs when one procedure directly returns the result of invoking another procedure; tail recursion occurs when a procedure recursively tail-calls itself, directly or indirectly. Scheme implementations are required to implement tail calls as jumps (gotos), so the storage overhead normally associated with recursion is avoided. As a result, Scheme programmers need master only simple procedure calls and recursion and need not be
burdened with the usual assortment of looping constructs.
和多数支持过程的语言类似,Scheme的过程可以被递归调用。这就是说,任何过程可以直接或简接的调用自己。许多算法可以用递归的形式高雅或高效地定义出来。一种递归特殊情形,被称为尾递归,被用来表达迭代或循环。一个尾调用总是发生在一个过程在尾部,直接或简接地递归地调用自身。Scheme的实现被要求尾调用要以jumps(goto) 的方式实现,这样通常可避免与递归有关的存储开销。作为一个结果,Scheme程序员仅需掌握简单过程调用和递归,而无需通常负担常见的多种循环结构。
Scheme supports the definition of arbitrary control structures with continuations. A continuation is a procedure that embodies the remainder of a program at a given point in the program. A continuation may be obtained at any time during the execution of a program. As with other procedures, a continuation is a first-class object and may be invoked at any time after its creation. Whenever it is invoked, the program immediately continues from the point where the continuation was obtained. Continuations allow the implementation of complex control mechanisms including explicit backtracking, multithreading, and coroutines.
Scheme支持定义带有continuation的任意控制结构。一个continuation是一个特殊的过程,它在程序的一个位置可以包含其剩余(尚未执行的)部分。在程序执行的任何时间都可以获取一个continuation。与其它过程一样,一个continuation也是第一类对象,而且可以在被创建后的任何时候调用。一旦被调用,程序会立即从continuation所得到的那个位置开始继续执行。Continuation允许实现复杂的控制机制,包括回溯、多线程和协程。
Scheme also allows programmers to define new syntactic forms, or syntactic extensions, by writing transformation procedures that determine how each new syntactic form maps to existing syntactic forms. These transformation procedures are themselves expressed in Scheme with the help of a convenient high-level pattern language that automates syntax checking, input deconstruction, and output reconstruction. By default, lexical scoping is maintained through the transformation process, but the programmer can exercise control over the scope of all identifiers appearing in the output of a transformer. Syntactic extensions are useful for defining new language constructs, for emulating language constructs found in other languages, for achieving the effects of in-line code expansion, and even for emulating entire languages in Scheme. Most large Scheme programs are built from a mix of syntactic extensions and procedure definitions.
Scheme也允许程序员定义新的语法形式,或者语法扩展,每个新的语法形式是通过编写变形器确定如何映射到现存的语法形式的。这些变形器过程在Scheme中表达有助于一些方便的高级模式语言,包括自动语法检查、输入析构和输出重构等。默认情况下,词法作用域在语法变形过程中依然有效,但是程序员可以控制转换器输出的标识符的作用域。语法扩展对于定义新的语言结构,或者模拟一些其他语言中的语言结构,或者获得一些内联代码展开,甚至包括模拟整个编程语言都非常有用。多数大型Scheme程序都是从一组语法扩展和过程定义中构建出来的。
Scheme evolved from the Lisp language and is considered to be a dialect of Lisp. Scheme inherited from Lisp the treatment of values as first-class objects, several important data types, including symbols and lists, and the representation of programs as objects, among other things. Lexical scoping and block structure are features taken from Algol 60 [21]. Scheme was the first Lisp dialect to adopt lexical scoping and block structure, first-class procedures, the treatment of tail calls as jumps, continuations, and lexically scoped syntactic extensions.
Scheme从LISP语言演化而来,也被认为是LISP的一种方言。Scheme语言继承了LISP处理第一类对象数值的方式,一些重要的数据类型,包括符号和列表,以及程序作为对象的表示,还有其他一些。词法作用域和块结构是从Algo 60[21]语言中借鉴过来的。Scheme是第一种采用了词法作用域和块结构、第一类过程对象以及将尾调用处理成jumps的LISP方言。
Common Lisp [27] and Scheme are both contemporary Lisp languages, and the development of each has been influenced by the other. Like Scheme but unlike earlier Lisp languages, Common Lisp adopted lexical scoping and first-class procedures, although Common Lisp's syntactic extension facility does not respect lexical scoping. Common Lisp's evaluation rules for procedures are different from the evaluation rules for other objects, however, and it maintains a separate namespace for procedure variables, thereby inhibiting the use of procedures as first-class objects. Also, Common Lisp does not support continuations or require proper treatment of tail calls, but it does support several less general control structures not found in Scheme. While the two languages are similar, Common Lisp includes more specialized constructs, while Scheme includes more general-purpose building blocks out of which such constructs (and others) may be built.
Common Lisp [27] 和Scheme都是现代的LISP语言,两者互相影响中发展。像Scheme,但又不像早期Lisp语言,Common Lisp采用了词法作用域和第一类过程,不过Common Lisp的语法扩展工具却不支持词法作用域。Common Lisp的过程求值规则和其他对象的求值规则不太一样,尽管如此,它仍然保持了针对过程变量的分离命名空间(namespace),所以这不便于将过程当作第一类对象使用。Common Lisp也不支持continuation或者要求对尾调用的处理,但是它支持少数Scheme中没有的通用控制结构。由于两种语言非常相似,Common Lisp包括了一些特殊结构,而对应的Scheme则提供了一些更一般的通用构建块,可以构造出前面Common Lisp提供的那些结构。
译者说明:Common Lisp定义过程(DEFUN)和定义普通变量(DEFVAR)的时候需要用不同的过程,对过程变量赋值自然和对过程变量赋值就存在区别了。
The remainder of this chapter describes Scheme's syntax and naming conventions and the typographical conventions used throughout this book.
本章剩余部分讲述了Scheme的语法和命名规范,以及全书的书写约定。
1.1 Scheme 语法(Scheme Syntax)
Scheme programs are made up of keywords, variables, structured forms, constant data (numbers, characters, strings, quoted vectors, quoted lists, quoted symbols, etc.), whitespace, and comments.
Scheme程序由关键词、变量、结构化形式、常量数据(数、字符、字符串、引用向量、引用列表和引用符号等)、空格和注释组成。
Keywords, variables, and symbols are collectively called identifiers. Identifiers may be formed from letters, digits, and certain special characters, including
?
,!
,.
,+
,-
,*
,/
,<
,=
,>
,:
,$
,%
,^
,&
,_
,~
, and@
, as well as a set of additional Unicode characters. Identifiers cannot start with an at sign@
and normally cannot start with any character that can start a number, i.e., a digit, plus sign+
, minus sign-
, or decimal point.
. Exceptions are+, -
, and ..., which are valid identifiers, and any identifier starting with->
. For example,hi, Hello, n, x, x3, x+2, and ?$&*!!!
are all identifiers. Identifiers are delimited by whitespace, comments, parentheses, brackets, string (double) quotes"
, and hash marks#
. A delimiter or any other Unicode character may be included anywhere within the name of an identifier as an escape of the form\xsv
;, wheresv
is the scalar value of the character in hexadecimal notation.
关键词、变量和符号统一被称为标识符。标识符可以由字母、数字和某些特定的符号所组成,这些符号包括?
, !
, .
, +
, -
, *
, /
, <
, =
, >
, :
, $
, %
, ^
, &
, _
, ~
和@
符号,还有一些额外的Unicode字符。标识符不能由符号@
开始,通常(译者:有些也是可以的,可以自己尝试)不能从任何可能表示数字开始的符号开始(译者:标识符不可以被当作数字先解析),例如一个数字、加号+
、减号-
或者小数点.
。除此之外的都是有效标识符,而且任何标识符可以从->
开始(译者:这是特殊规定,大概其他的没有特殊规定)。例如:hi
、Hello
、n
、x
、x3
、x+2
还有?$&*!!!
(译者:这什么鬼?)这种都可以是合法的标识符。一个标识符或者任何其他Unicode字符都可以在任何位置作为标识符,其形式需要用转义形式写如\xsv
,其中sv
是这个字符的十六进制表示的标量数值。
There is no inherent limit on the length of a Scheme identifier; programmers may use as many characters as necessary. Long identifiers are no substitute for comments, however, and frequent use of long identifiers can make a program difficult to format and consequently difficult to read. A good rule is to use short identifiers when the scope of the identifier is small and longer identifiers when the scope is larger.
Scheme的标识符没有长度限制;程序员可以按需使用任何字符。长标识符并不能替代注释,而且,频繁使用长标识符可能导致程序难以格式化和难于阅读。一个不错的规则是:当标识符的作用域比较小的时候采用短小标识符,当作用域较大时则采用长标识符。
Identifiers may be written in any mix of upper- and lower-case letters, and case is significant, i.e., two identifiers are different even if they differ only in case. For example,
abcde
,Abcde
,AbCdE
, andABCDE
all refer to different identifiers. This is a change from previous versions of the Revised Report.
标识符可以任意混合大小写字母,而且区分大小写字母,也就是说两个标识符即使只是大小写差异也算是不同的标识符。例如abcde
、Abcde
、AbCdE
、ABCDE
是不同的标识符。这和之前版本的Revised Report是不同的。
Structured forms and list constants are enclosed within parentheses, e.g.,
(a b c)
or(* (- x 2)
y). The empty list is written (). Matched sets of brackets[ ]
may be used in place of parentheses and are often used to set off the subexpressions of certain standard syntactic forms for readability, as shown in examples throughout this book. Vectors are written similarly to lists, except that they are preceded by#(
and terminated by)
, e.g.,#(this is a vector of symbols)
. Bytevectors are written as sequences of unsigned byte values (exact integers in the range 0 through 255) bracketed by#vu8(
and)
, e.g., #vu8(3 250 45 73).
结构化形式和列表常量必须用圆括号括起来,如(a b c)
或(* (- x 2) y)
。空表写作()
。方括号[]
可以用来替代方括号,常用于一些标准语法形式的子表达式中,可以改善可读性,本书会给出一些例子。向量(Vector)类似列表,只是以#(
开始,)
结束,例如#(this is a vector of symbols)
。字节向量(Bytevector)写作一个无符号字节值(0到255之间的整数),并用#vu8(
和)
括起来,如#vu(3 250 45 73)
。
Strings are enclosed in double quotation marks, e.g.,
"I am a string"
. Characters are preceded by#\
, e.g.,#\a
. Case is important within character and string constants, as within identifiers. Numbers may be written as integers, e.g.,-123
, as ratios, e.g.,1/2
, in floating-point or scientific notation, e.g.,1.3
or1e23
, or as complex numbers in rectangular or polar notation, e.g.,1.3-2.7i
or-1.2@73
. Case is not important in the syntax of a number. The boolean values representing true and false are written#t
and#f
. Scheme conditional expressions actually treat#f
as false and all other objects as true, so3
,0
,()
,"false"
, andnil
all count as true.
字符串用双引号括起来,如"I am a string"
。字符用#\
开头,例如#\a
表示字符a
。大小写对于字符和字符串常量是很重要的,和标识符一样。数值可以写成整数,如-123
,也可以是分数,如1/2
,在浮点或者科学计数法中写作1.3
或1e23
,此外复数数值可以用矩形记法或者幅角记法,如1.3-2.7i
或者-1.2@73
(译者:这里幅角记法中的角度是弧度值)。数值写法中的字母大小写不重要。布尔值中的“真”(true)和“假”(false)分别写作#t
和#f
。Scheme条件表达式一般都将#f
作为假,而其他对象都作为真,例如3
、0
、()
、"false"
和nil
等全部当作真来处理。
Details of the syntax for each type of constant data are given in the individual sections of Chapter 6.
关于每种常数类型的语法在第6章的一节中给出。
Scheme expressions may span several lines, and no explicit terminator is required. Since the number of whitespace characters (spaces and newlines) between expressions is not significant, Scheme programs should be indented to show the structure of the code in a way that makes the code as readable as possible. Comments may appear on any line of a Scheme program, between a semicolon
;
and the end of the line. Comments explaining a particular Scheme expression are normally placed at the same indentation level as the expression, on the line before the expression. Comments explaining a procedure or group of procedures are normally placed before the procedures, without indentation. Multiple comment characters are often used to set off the latter kind of comment, e.g.,;;; The following procedures ....
Scheme 表达式可以展开成多行,且不需要显式的终结符。因为空白字符的数量没有要求,Scheme程序应当通过恰当的缩进来体现代码的结构,这样才能使代码更加具有可读性。注释可以出现在Scheme程序的任何一行,使用分号;
直到改行结束。解释一段表达式的注释通常置于该表达式同等缩进级别的前一行。用来注释一个过程、或者一组过程的内容通常放在这个过程的前面,且不带缩进。多个缩进字符通常用于后面这种注释,如:;;; The following procedures ....
Two other forms of comments are supported: block comments and datum comments. Block comments are delimited by
#|
and|#
pairs, and may be nested. A datum comment consists of a#;
prefix and the datum (printed data value) that follows it. Datum comments are typically used to comment out individual definitions or expressions. For example,(three #;(not four) element list)
is just what it says. Datum comments may also be nested, though#;#;(a)(b)
has the somewhat nonobvious effect of commenting out both(a)
and(b)
.
Scheme还支持另外两种注释:块注释和数据注释。块注释使用#|
和|#
对,并且支持嵌套注释;一个数据注释由前缀#;
和数据(打印值)跟随。数据注释经常用于对单个的定义或者表达式进行注释。例如(three #;(not four) element list)
正是如此。数据注释也可以被嵌套,所以#;#;(a)(b)
某种意义上注释效果不如直接注释(a)
和(b)
。
Some Scheme values, such as procedures and ports, do not have standard printed representations and can thus never appear as a constant in the printed syntax of a program. This book uses the notation
#<_description_>
when showing the output of an operation that returns such a value, e.g.,#<procedure>
or#<port>
.
某些Scheme的数值,诸如过程和端口,没有标准的打印表示,也没有对应的程序中的可打印语法(译者:用程序代码写出、表示)。这本书使用#<_description_>#
来显式这类值,比如#<procedure>
或者#<port>
。
1.2 Scheme 命名规范(Scheme Naming Convention)
Scheme's naming conventions are designed to provide a high degree of regularity. The following is a list of these naming conventions:
Predicate names end in a question mark?
. Predicates are procedures that return a true or false answer, such aseq?
,zero?
, andstring=?
. The common numeric comparators=
,<
,>
,<=
, and>=
are exceptions to this naming convention.
Type predicates, such aspair?
, are created from the name of the type, in this case pair, and the question mark.
The names of most character, string, and vector procedures start with the prefixchar-
,string-
, andvector-
, e.g., string-append. (The names of some list procedures start withlist-
, but most do not.)
The names of procedures that convert an object of one type into an object of another type are written astype1->type2
, e.g.,vector->list
.
The names of procedures and syntactic forms that cause side effects end with an exclamation point!
. These includeset!
andvector-set!
. Procedures that perform input or output technically cause side effects, but their names are exceptions to this rule.
Programmers should employ these same conventions in their own code whenever possible.
Scheme的命名规范被设计具有高度的规范性。下面是命名规范的一个列表:
- 谓词名称结尾总是问号
?
。谓词是返回值为布尔值(要么为真,要么为假)的一类过程,例如eq?
、zero?
还有string=?
。常见的数值比较操作=
、<
、>
、<=
、>=
则是例外。 - 类型谓词如
pair?
是从类型名和问号组成。 - 多数字符、字符串和向量过程的名称以
char-
、string-
和vector-
开始。 - 将一个类型的对象转换为另一个类型的过程的名字则写作
type1->type2
,例如vector->list
。 - 会产生副作用的过程和语法形式的名字则以惊叹号
!
结尾。比如有set!
和vector-set!
。处理输入和输出的过程技术上会产生副作用,但是它们的名字不遵守这条规则。
程序员应当尽可能的使自己的代码采用上述约定。
1.3 印刷和记号的约定(Typographical and Notational Conventions)
A standard procedure or syntactic form whose sole purpose is to perform some side effect is said to return unspecified. This means that an implementation is free to return any number of values, each of which can be any Scheme object, as the value of the procedure or syntactic form. Do not count on these values being the same across implementations, the same across versions of the same implementation, or even the same across two uses of the procedure or syntactic form. Some Scheme systems routinely use a special object to represent unspecified values. Printing of this object is often suppressed by interactive Scheme systems, so that the values of expressions returning unspecified values are not printed.
一个标准的过程或者语法形式,如果它的目的就是产生某些副作用,那么它的返回值是未被规定的。这就是说一个Scheme实现可以自行规定此类对象返回任何数值或者任何Scheme对象,也可以是过程或者语法形式。这些值在相同的实现、相同实现的多个版本之间、甚至这些过程或语法形式的两次使用中会相同。一些Scheme系统使用了特殊对象用来表示这些未规定的数值。在交互式的Scheme系统中,一般不打印这个对象,所以返回这种未指定值的表达式不被打印。
While most standard procedures return a single value, the language supports procedures that return zero, one, more than one, or even a variable number of values via the mechanisms described in Section 5.8. Some standard expressions can evaluate to multiple values if one of their subexpressions evaluates to multiple values, e.g., by calling a procedure that returns multiple values. When this situation can occur, an expression is said to return "the values" rather than simply "the value" of its subexpression. Similarly, a standard procedure that returns the values resulting from a call to a procedure argument is said to return the values returned by the procedure argument.
多数标准过程返回单一值,语言也支持返回零个、一个、或多个值,甚至通过5.8节中描述的机制可以返回可变数量的值。一些标准表达式也可能求值得到多个值,如果它的一个子表达式求值得到了多个返回值,例如调用一个返回多值的过程。当这种情况发生的时候,我们称这个表达式返回了多个值,而不是简单的说是它的子表达式的返回值。类似的,一个标准过程若其返回值是其调用一个过程参数的,被称为其返回了由过程参数返回的值。
This book uses the words "must" and "should" to describe program requirements, such as the requirement to provide an index that is less than the length of the vector in a call to
vector-ref
. If the word "must" is used, it means that the requirement is enforced by the implementation, i.e., an exception is raised, usually with condition type &assertion. If the word "should" is used, an exception may or may not be raised, and if not, the behavior of the program is undefined.
此书使用“必须”和“应当”来描述程序的要求,例如要求调用vector-ref
时传入的索引必须小于相应向量的长度。如果使用了“必须”一词,意味着该Scheme实现是强制的,例如一个异常被抛出,通常带有条件类型&assertion
。如果“应当”被使用,那么异常也许或未必被抛出,如若不是的话这个程序行为就没有被定义。
The phrase "syntax violation" is used to describe a situation in which a program is malformed. Syntax violations are detected prior to program execution. When a syntax violation is detected, an exception of type &syntax is raised and the program is not executed.
短语“违反语法”被用于描述一段程序出现了病态。违反语法的情况会在程序执行前就被检测出来。当一个违反语法的情况被检测出来,一个类型&syntax
的异常就会被抛出,而此时程序尚未执行。
The typographical conventions used in this book are straightforward. All Scheme objects are printed in a typewriter typeface, just as they are to be typed at the keyboard. This includes syntactic keywords, variables, constant objects, Scheme expressions, and example programs. An italic typeface is used to set off syntax variables in the descriptions of syntactic forms and arguments in the descriptions of procedures. Italics are also used to set off technical terms the first time they appear. In general, names of syntactic forms and procedures are never capitalized, even at the beginning of a sentence. The same is true for syntax variables written in italics.
本书中的印刷格式的约定很简单。所有的Scheme对象用打印机字体,如同从键盘中打出来的。包括语法关键词、变量、常量对象、Scheme表达式和例程。斜体用于表达语法形式的语法变量和过程中的参数。斜体也可以被用于一些术语第一次出现的位置。一般来说,语法形式和过程的名字不会首字母大写,即使在一句话的最前。这对于以斜体书写的语法变量相同。
In the description of a syntactic form or procedure, one or more prototype patterns show the syntactic form or forms or the correct number or numbers of arguments for an application of the procedure. The keyword or procedure name is given in typewriter font, as are parentheses. The remaining pieces of the syntax or arguments are shown in italics, using a name that implies the type of expression or argument expected by the syntactic form or procedure. Ellipses are used to specify zero or more occurrences of a subexpression or argument. For example,
(or
expr...)
describes theor
syntactic form, which has zero or more subexpressions, and(member
obj list)
describes themember
procedure, which expects two arguments, an object and a list.
在语法形式或过程的描述中,一个或者多个原型模式给出应用过程时一种正确或多种正确的参数数量。关键词或者过程名称为打印机字体,包括括号。语法的剩余部分或者参数用斜体表示,使用一个可以暗示该语法形式或过程所需参数或表达式的类型的名称。省略号表示一个子表达式或者参数出现0次或者多次。例如(or
expr ...)
表示or
的语法形式,后续参数可以为0个或多个子表达式,而(member
obj list )
表示了member
这个过程需要两个参数,一个是对象,一个是列表。
A syntax violation occurs if the structure of a syntactic form does not match its prototype. Similarly, an exception with condition type
&assertion
is raised if the number of arguments passed to a standard procedure does not match what it is specified to receive. An exception with condition type&assertion
is also raised if a standard procedure receives an argument whose type is not the type implied by its name or does not meet other criteria given in the description of the procedure. For example, the prototype forvector-set!
is
(vector-set!
vector n obj)
and the description says that n must be an exact nonnegative integer strictly less than the length of vector. Thus,vector-set!
must receive three arguments, the first of which must be a vector, the second of which must be an exact nonnegative integer less than the length of the vector, and the third of which may be any Scheme value. Otherwise, an exception with condition type &assertion is raised.
若一段语法结构不匹配其圆形时,将发生违法语法的情况。类似的,如果传入标准过程的参数不匹配其规定,则会抛出一个带有条件类型&assertion
的异常。同样的,一个标准过程如果接收到的参数类型与其名字所指的类型不匹配或者无法满足其他判断准则时,也会产生这样的遗传。例如,下列vector-set!
原型:
(vector-set!
vector n obj)
着段表示中n必须是一个非负整数(严格小于vector的长度)。所以,vector-set!
必须是三个参数,第一个是一个向量,第二个必须是一个非负整数,切小于该向量的长度,第三个参数则可以是任何Scheme值。不满足上述条件时,将会产生一个带有条件类型&assertion
的异常 。
In most cases, the type of argument required is obvious, as with vector, obj, or binary-input-port. In others, primarily within the descriptions of numeric routines, abbreviations are used, such as int for integer, exint for exact integer, and fx for fixnum. These abbreviations are explained at the start of the sections containing the affected entries.
在多数情况下,参数类型是显然的,例如vector、obj或者binary-input-port。另一些情况下,主要是一些数值类程序,一般使用简写,如int对应整数,exint对应精确整数,fx对应定点数。这些简写可以在有关章节的开头部分找到解释。