提交 a7505fd8 编写于 作者: W wizardforcel

3.6,1

上级 acb8573a
......@@ -111,5 +111,425 @@ x ; x.
33 ; add function and * to primitive multiply.
```
**基本的特殊形式。**特殊形式将东西表示为 Python 中的控制结构、函数调用或者类的定义:在调用时,这些结构不会简单地立即求值。
首先,一些通用的结构以这种形式使用:
`EXPR-SEQ`
只是表达式的序列,例如:
```scheme
(+ 3 2) x (* y z)
```
当它出现在下面的定义中时,它指代从左到右求值的表达式序列,最后一个表达式的序列值就是它的值。
`BODY`
一些结构拥有“主体”,它们是 `EXPR-SEQ`,就像上面一次,可能由一个或多个定义处理。它们的值就是 `EXPR-SEQ` 的值。这些定义的解释请见内部定义一节。
下面是这些特殊形式的代表性子集:
定义
定义可以出现在程序的顶层(也就是不包含在其它结构中)。
`(define SYM EXPR)`
求出`EXPR`并在当前环境将其值绑定到符号`SYM`上。
`(define (SYM ARGUMENTS) BODY)`
等价于`(define SYM (lambda (ARGUMENTS) BODY))`
`(lambda (ARGUMENTS) BODY)`
求值为函数。`ARGUMENTS `通常为(可能非空的)不同符号的列表,向函数提供参数名称,并且表明它们的数量。`ARGUMENTS`也可能具有如下形式:
```scheme
(sym1 sym2 ... symn . symr)
```
(也就是说,列表的末尾并不像普通列表那样是空的,最后的`cdr`是个符号。)这种情况下,`symr`会绑定到列表的尾后参数值(后面的第 n+1 个参数)。
当产生的函数被调用时,`ARGUMENTS`在一个新的环境中绑定到形参的值上,新的环境扩展自`lambda`表达式求值的环境(就像 Python 那样)。之后`BODY`会被求值,它的值会作为调用的值返回。
`(if COND-EXPR TRUE-EXPR OPTIONAL-FALSE-EXPR)`
求出`COND-EXPR`,如果它的值不是`#f`,那么求出`TRUE-EXPR`,结果会作为`if`的值。如果`COND-EXPR`值为`#f`而且`OPTIONAL-FALSE-EXPR`存在,它会被求值为并作为`if`的值。如果它不存在,`if`值是未定义的。
`(set! SYMBOL EXPR)`
求出`EXPR`使用该值替换`SYMBOL `的绑定。`SYMBOL `必须已经绑定,否则会出现错误。和 Python 的默认情况不同,它会在定义它的第一个环境帧上替换绑定,而不总是最深处的帧。
`(quote EXPR)``'EXPR`
将 Scheme 数据结构用于程序表示的一个问题,是需要一种方式来表示打算被求值的程序文本。`quote`形式求值为`EXPR`自身,而不进行进一步的求值(替代的形式使用前导的单引号,由 Scheme 表达式读取器转换为第一种形式)。例如:
```scheme
>>> (+ 1 2)
3
>>> '(+ 1 2)
(+ 1 2)
>>> (define x 3)
x
>>> x
3
>>> (quote x)
x
>>> '5
5
>>> (quote 'x)
(quote x)
```
**派生的特殊形式**
派生结构时可以翻译为基本结构的结构。它们的目的是让程序对于读取器更加简洁可读。在 Scheme 中:
`(begin EXPR-SEQ)`
简单地求值并产生`EXPR-SEQ`的值。这个结构是个简单的方式,用于在需要单个表达式的上下文中执行序列或表达式。
`(and EXPR1 EXPR2 ...)`
每个`EXPR`从左到右执行,直到碰到了`#f`,或遍历完`EXPRs`。值是最后求值的`EXPR`,如果`EXPRs`列表为空,那么值为`#t`。例如:
```scheme
>>> (and (= 2 2) (> 2 1))
#t
>>> (and (< 2 2) (> 2 1))
#f
>>> (and (= 2 2) '(a b))
(a b)
>>> (and)
#t
```
`(or EXPR1 EXPR2 ...)`
每个`EXPR`从左到右求值碰到了不为`#f`的值,或遍历完`EXPRs`。值为最后求值的`EXPR`,如`EXPRs`列表为空,那么值为`#f`。例如:
```scheme
>>> (or (= 2 2) (> 2 3))
#t
>>> (or (= 2 2) '(a b))
#t
>>> (or (> 2 2) '(a b))
(a b)
>>> (or (> 2 2) (> 2 3))
#f
>>> (or)
#f
```
`(cond CLAUSE1 CLAUSE2 ...)`
每个`CLAUSEi`都依次处理,直到其中一个处理成功,它的值就是`cond`的值。如果没有子句处理成功,值是未定义的。每个子句都有三种可能的形式。
如果`TEST-EXPR `求值为不为`#f`的值,`(TEST-EXPR EXPR-SEQ)`形式执行成功。这种情况下,它会求出`EXPR-SEQ`并产生它的值。`EXPR-SEQ`可以不写,这种情况下值为`TEST-EXPR`本身。
最后一个子句可为`(else EXPR-SEQ)`的形式,它等价于`(#t EXPR-SEQ)`
最后,`(TEST_EXPR => EXPR)`的形式在`TEST_EXPR`求值为不为`#f`的值(叫做`V`)时处理成功。如果成功了,`cond`结构的值是由`(EXPR V)`返回的值。也就是说,`EXPR`必须求值为单参数的函数,在`TEST_EXPR`的值上调用。
例如:
```scheme
>>> (cond ((> 3 2) 'greater)
... ((< 3 2) 'less)))
greater
>>> (cond ((> 3 3) 'greater)
... ((< 3 3) 'less)
... (else 'equal))
equal
>>> (cond ((if (< -2 -3) #f -3) => abs)
... (else #f))
3
```
`(case KEY-EXPR CLAUSE1 CLAUSE2 ...)`
求值`KEY-EXPR`会产生一个值`K`。之后将`K`与每个`CLAUSEi`一次匹配,直到其中一个成功,并且返回该子句的值。如果没有子句成功,值是未定义的。每个子句都拥有`((DATUM1 DATUM2 ...) EXPR-SEQ)`的形式。其中`DATUMs`是 Scheme 值(它们不会被求值)。如果`K`匹配了`DATUM`的值之一(由下面描述的`eqv?`函数决定),子句就会成功,它的`EXPR-SEQ`就会被求值,并且它的值会变成`case`的值。最后的子句可为`(else EXPR-SEQ)`的形式,它总是会成功,例如:
```scheme
>>> (case (* 2 3)
... ((2 3 5 7) 'prime)
... ((1 4 6 8 9) 'composite))
composite
>>> (case (car '(a . b))
... ((a c) 'd)
... ((b 3) 'e))
d
>>> (case (car '(c d))
... ((a e i o u) 'vowel)
... ((w y) 'semivowel)
... (else 'consonant))
consonant
```
`(let BINDINGS BODY)`
`BINDINGS`是偶对的列表,形式为:
```scheme
( (VAR1 INIT1) (VAR2 INIT2) ...)
```
其中`VARs`是(不同的)符号,而`INITs`是表达式。首先会求出`INIT`表达式,之后创建新的帧,将这些值绑定到`VARs`,再然后在新环境中求出`BODY`,返回它的值。换句话说,它等价于调用
```scheme
((lambda (VAR1 VAR2 ...) BODY)
INIT1 INIT2 ...)
```
所以,任何`INIT`表达式中的`VARs`引用都指向这些符号在`let`结构外的定义(如果存在的话),例如:
```scheme
>>> (let ((x 2) (y 3))
... (* x y))
6
>>> (let ((x 2) (y 3))
... (let ((x 7) (z (+ x y)))
... (* z x)))
35
```
`(let* BINDINGS BODY)`
`BINDINGS `的语法和`let`相同。它等价于
```scheme
(let ((VAR1 INIT1))
...
(let ((VARn INITn))
BODY))
```
也就是说,它就像`let`表达式那样,除了`VAR1`的新绑定对`INITs`子序列以及`BODY`中可见,`VAR2`与之类似,例如:
```scheme
>>> (define x 3)
x
>>> (define y 4)
y
>>> (let ((x 5) (y (+ x 1))) y)
4
>>> (let* ((x 5) (y (+ x 1))) y)
6
```
`(letrec BINDINGS BODY)`
同样,语法类似于`let`。这里,首先会创建新的绑定(带有未定义的值),之后`INITs`被求值并赋给它们。如果某个`INITs`使用了某个`VAR`的值,并且没有为其赋初始值,结果是未定义的。这个形式主要用于定义互相递归的函数(lambda 本身并不会使用它们提到过的值;这只会在它们被调用时随后发生)。例如:
```scheme
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88))
```
**内部定义。**`BODY``define`结构的序列开始时,它们被看作“内部定义”,并且在解释上与顶层定义有些不同。特别是,它们就像`letrec`那样。
+ 首先,会为所有有`define`语句定义的名称创建绑定,一开始绑定到未定义的值上。
+ 之后,值由定义来填充。
所以,内部函数定义的序列是互相递归的,就像 `Python 中嵌套在函数中的`def`语句那样:
```scheme
>>> (define (hard-even? x) ;; An outer-level definition
... (define (even? n) ;; Inner definition
... (if (zero? n)
... #t
... (odd? (- n 1))))
... (define (odd? n) ;; Inner definition
... (if (zero? n)
... #f
... (even? (- n 1))))
... (even? x))
>>> (hard-even? 22)
#t
```
**预定义函数。**预定义函数有很多,都在全局环境中绑定到名称上,我们只会展示一小部分。其余的会在[ Revised(4) Scheme 报告](http://people.csail.mit.edu/jaffer/r4rs_toc.html)中列出。函数调用并不是“特殊的”,因为它们都使用相同的完全统一的求值规则:递归求出所有项目(包括运算符),并且之后在操作数的值上调用运算符的值(它必须是个函数)。
+ **算数:**Scheme 提供了标准的算数运算符,许多都拥有熟悉的表示,虽然它们统一出现在操作数前面:
```scheme
>>> ; Semicolons introduce one-line comments.
>>> ; Compute (3+7+10)*(1000-8) // 992 - 17
>>> (- (quotient (* (+ 3 7 10) (- 1000 8))) 17)
3
>>> (remainder 27 4)
3
>>> (- 17)
-17
```
与之相似,存在通用的数学比较运算符,扩展为可接受多于两个参数:
```scheme
>>> (< 0 5)
#t
>>> (>= 100 10 10 0)
#t
>>> (= 21 (* 7 3) (+ 19 2))
#t
>>> (not (= 15 14))
#t
>>> (zero? (- 7 7))
#t
```
随便提一下,`not`是个函数,并不是`and`或`or`的特殊形式,因为他的运算符必须求值,所以不需要特殊对待。
+ **列表和偶对。**很多操作用于处理偶对和列表(它们也使用偶对和空列表构建)。
```scheme
>>> (cons 'a 'b)
(a . b)
>>> (list 'a 'b)
(a b)
>>> (cons 'a (cons 'b '()))
(a b)
>>> (car (cons 'a 'b))
a
>>> (cdr (cons 'a 'b))
b
>>> (cdr (list a b))
(b)
>>> (cadr '(a b)) ; An abbreviation for (car (cdr '(a b)))
b
>>> (cddr '(a b)) ; Similarly, an abbreviation for (cdr (cdr '(a b)))
()
>>> (list-tail '(a b c) 0)
(a b c)
>>> (list-tail '(a b c) 1)
(b c)
>>> (list-ref '(a b c) 0)
a
>>> (list-ref '(a b c) 2)
c
>>> (append '(a b) '(c d) '() '(e))
(a b c d e)
>>> ; All but the last list is copied. The last is shared, so:
>>> (define L1 (list 'a 'b 'c))
>>> (define L2 (list 'd))
>>> (define L3 (append L1 L2))
>>> (set-car! L1 1)
>>> (set-car! L2 2)
>>> L3
(a b c 2)
>>> (null? '())
#t
>>> (list? '())
#t
>>> (list? '(a b))
#t
>>> (list? '(a . b))
#f
```
+ **相等性:**`=`运算符用于数值。一般对于值的相等性,Scheme 区分`eq?`(就像 Python 的`is`),`eqv?`(与之类似,但是和数值上`=`一样),和`equal?`(比较列表结构或字符串的内容)。通常来说,除了在类似比较符号、布尔值或者空列表的情况中,我们都使用`eqv?`和`equal?`。
```scheme
>>> (eqv? 'a 'a)
#t
>>> (eqv? 'a 'b)
#f
>>> (eqv? 100 (+ 50 50))
#t
>>> (eqv? (list 'a 'b) (list 'a 'b))
#f
>>> (equal? (list 'a 'b) (list 'a 'b))
#t
```
+ **类型。**每个值的类型都只满足一个基本的类型断言。
```scheme
>>> (boolean? #f)
#t
>>> (integer? 3)
#t
>>> (pair? '(a b))
#t
>>> (null? '())
#t
>>> (symbol? 'a)
#t
>>> (procedure? +)
#t
```
+ **输入和输出:**Scheme 计时器通常执行“读取-求值-打印”循环,但是我们可以在显式的程序控制下输出东西,使用与解释器内部相同的函数:
```scheme
>>> (begin (display 'a) (display 'b) (newline))
ab
```
于是,`(display x)`与 Python 的
```py
print(str(x), end="")
```
相似,并且`(newline)`类似于`print()`。
对于输入来说,`(read)`从当前“端口”读取 Scheme 表达式。它并不会解释表达式,而是将其读作数据:
```scheme
>>> (read)
>>> (a b c)
(a b c)
```
+ **求值。**`apply `函数提供了函数调用运算的直接访问:
```scheme
>>> (apply cons '(1 2))
(1 . 2)
>>> ;; Apply the function f to the arguments in L after g is
>>> ;; applied to each of them
>>> (define (compose-list f g L)
... (apply f (map g L)))
>>> (compose-list + (lambda (x) (* x x)) '(1 2 3))
14
```
存在允许开头出现“固定”参数的扩展:
```scheme
>>> (apply + 1 2 '(3 4 5))
15
```
下面的函数并不在 [Revised(4) Scheme](http://people.csail.mit.edu/jaffer/r4rs_toc.html) 中,但是存在于我们的解释器版本中(警告:非标准的过程在 Scheme 的后续版本中并不以这种形式定义):
```scheme
>>> (eval '(+ 1 2))
3
```
也就是说,eval`求解一块 Scheme 数据,它表示正确的 Scheme 表达式。这个版本在全局环境中求解表达式的参数。我们的解释器也提供了一种方式,来规定求值的特定环境:
```scheme
>>> (define (incr n) (lambda (x) (+ n x)))
>>> (define add5 (incr 5))
>>> (add5 13)
18
>>> (eval 'n (procedure-environment add5))
5
```
## 3.6.2 Logo 语言
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册