原文:表达式与定义
Racket Essentials章节介绍了Racket的一些句法形式:定义,过程应用,条件等。 本节提供有关这些内容的更多详细信息,以及一些其他基本形式。
这一章(以及本文档的剩下部分)使用一种稍微不同于Racket Essentials的符号,后者的文法是基于字符
的.向下面这样:
(something [id …+] an-expr …)
本说明书中的斜体元变量(meta-variables), 如id和an-expr, 使用Racket标识符的语法, 因此an-expr是一个元变量。 命名约定隐含地定义了许多元变量的含义:
- 一个元变量,以id结尾, 是一个标识符的缩写, 如 x
,my-favorite-martian
.
- 一个元标识符(meta-identifier), 以keyword结尾的,是一个keyword
的缩写, 如 #:tag
.
- 一个元标识符(meta-identifier), 以expr结尾的, 是任何sub-form
的缩写; 并且它将被解析为expression
.
- 一个元标识符(meta-identifier), 以body结尾的, 是任何sub-form
的缩写; 它要么被解析为本地绑定
(a local definition
). 一个body可以被解析为define
,只有在所有表达式之前,并且最最后一个body必须是表达式;见Internal Definitions.
// TODO
Square brackets in the grammar indicate a parenthesized sequence of forms, where square brackets are normally used (by convention). That is, square brackets do not mean optional parts of the syntactic form.
// TODO
>(let ([v (random 6)])
(printf "~a\n" v)
(case v
[(0) 'zero]
[(1) 'one]
[(2) 'two]
[(3 4 5) 'many]))
> (case (random 6)
[(0) 'zero]
[(1) 'one]
[(2) 'two]
[else 'many])
'many
parameterize
关联一个新值到一个parameter,在表达式被计算的时候:
(parameterize ([parameter-expr value-expr] …)
body …+)
例如,参数[error-print-width](https://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._error-print-width%29%29)
控制在错误消息中,打印多少了字符:
> (parameterize ([error-print-width 5])
(car (expt 10 1024)))
car: contract violation
expected: pair?
given: 10...
> (parameterize ([error-print-width 10])
(car (expt 10 1024)))
car: contract violation
expected: pair?
given: 1000000...
更一般地说,参数实现一种动态绑定.make-parameter
函数接受任何值,并返回一个初始化为给定值的新参数.将其当成函数应用返回它当前的值:
> (define location (make-parameter "here"))
> (location)
"here"
在parameterize form
中,每一个parameter-expr必须产生一个参数. 在body
的计算期间, 每一个具体的parameter
都给出一个相应的结果.当控制离开parameterize form
–不管是单通的return,一个异常,或其它方式–这个parameter恢复之前的值:
> (define location (make-parameter "here"))
> (parameterize ([location "there"])
(location))
"there"
> (location)
"here"
> (parameterize ([location "in a house"])
(list (location)
(parameterize ([location "with a mouse"])
(location))
(location)))
'("in a house" "with a mouse" "in a house")
> (parameterize ([location "in a box"])
(car (location)))
car: contract violation
expected: pair?
given: "in a box"
`parameterize
不是一个像 let
一样的绑定;上面的每一个对location
的引用,都是直接指向最初的定义. parameterize
在整个parameterize body
计算过程中调整参数的值,即使对该参数的使用,是在体之外的文本参数: TODO
> (define (would-you-could-you?)
(and (not (equal? (location) "here"))
(not (equal? (location) "there"))))
> (would-you-could-you?)
#f
> (parameterize ([location "on a bus"])
(would-you-could-you?))
#t
如果在 parameterize体内,使用一个文本化的参数,但是在parameterize产生值之前,没有进行计算,那么将不会看到这个由parameterize产生的值:
> (define location (make-parameter "here"))
> (let ([get (parameterize ([location "with a fox"])
(lambda () (location)))])
(get))
"here" ;; 为什么不是 "with a fox"?因为调用 (get)时,还没有计算 (location)的值
通过把parameter
当成一个函数进行调用并传一个参数, 可以调整当前绑定的值.如果parameterize
调整了parameter的值, 那么直接应用这个parameter过程仅仅影响关联的parameterize:
> (define (try-again! where)
(location where)) ;; 这里把 locaction录成一个具有一个参数的函数调用,就调整了它的值
> (location) ;; 此时值还是原来的值
"here"
> (parameterize ([location "on a train"])
(list (location) ;; 此时的值是 "on a train"
(begin (try-again! "in a boat") ;; 调了 try-again!后,值是 "in a boat"
(location))))
'("on a train" "in a boat")
Using parameterize is generally preferable to updating a parameter value imperatively—for much the same reasons that binding a fresh variable with let is preferable to using set! (see Assignment: set!).
使用parameterize
通常优于强制更新参数值–比使用 set!更好? TODO
看起来, 变量(variables)和set!
可以解决很多相同的问题,例如: location
可以被定义为一个string,并使用set!
调整它的值:
> (define lokation "here")
> (define (would-ya-could-ya?)
(and (not (equal? lokation "here"))
(not (equal? lokation "there"))))
> (set! lokation "on a bus")
> (would-ya-could-ya?)
#t
但是,相比于set!
,Parameters
提过了几个关键的特性:
- parameterize
自动恢复 parameter的值,当控制因为异常跳出时, 使用异常处理或其它形式,相对来说比较乏味.
- Parameters 可以使尾递规很好的工作(参见Tail Recursion). parameterize
中的最后一个body相对于参数化形式处于尾部位置。
- 参数使用线程正常工作(请参阅Threads)。 参数化窗体仅调整当前线程中的参数值,以避免与其他线程的竞争条件。