Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
sicp-py-zh
提交
a7505fd8
S
sicp-py-zh
项目概览
OpenDocCN
/
sicp-py-zh
9 个月 前同步成功
通知
1
Star
74
Fork
21
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
sicp-py-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
a7505fd8
编写于
9月 10, 2016
作者:
W
wizardforcel
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
3.6,1
上级
acb8573a
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
420 addition
and
0 deletion
+420
-0
3.6.md
3.6.md
+420
-0
未找到文件。
3.6.md
浏览文件 @
a7505fd8
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录