4 表达式与定义(Expressions and Definitions)

原文:表达式与定义

4 表达式与定义

Racket Essentials章节介绍了Racket的一些句法形式:定义,过程应用,条件等。 本节提供有关这些内容的更多详细信息,以及一些其他基本形式。

4.1 符号系统 Notation

这一章(以及本文档的剩下部分)使用一种稍微不同于Racket Essentials的符号,后者的文法是基于字符的.向下面这样:

(something [id …+] an-expr …)

本说明书中的斜体元变量(meta-variables), 如idan-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.

4.2 标识符和绑定Identifiers and Binding

4.3 函数调用(过程应用) Function Calls (Procedure Applications)

4.3.1 Evaluation Order and Arity

4.3.2 Keyword Arguments

4.3.3 The apply Function

4.4 函数(过程) Functions (Procedures): lambda

4.4.1 Declaring a Rest Argument

4.4.2 Declaring Optional Arguments

4.4.3 Declaring Keyword Arguments

4.4.4 Arity-Sensitive Functions: case-lambda

4.5 定义 Definitions: define

4.5.1 Function Shorthand

4.5.2 Curried Function Shorthand

4.5.3 Multiple Values and define-values

4.5.4 Internal Definitions

4.6 本地绑定 Local Binding

4.6.1 Parallel Binding: let

4.6.2 Sequential Binding: let*

4.6.3 Recursive Binding: letrec

4.6.4 Named let

4.6.5 Multiple Values: let-values, let*-values, letrec-values

4.7 条件 Conditionals

4.7.1 Simple Branching: if

4.7.2 Combining Tests: and and or

4.7.3 Chaining Tests: cond

4.8 序列 Sequencing

4.8.1 Effects Before: begin

4.8.2 Effects After: begin0

4.8.3 Effects If…: when and unless

4.9 赋值? Assignment: set!

4.9.1 Guidelines for Using Assignment

4.9.2 Multiple Values: set!-values

4.10 Quoting: quote and ‘

4.11 Quasiquoting: quasiquote and ‘

4.12 简单分发 Simple Dispatch: case

// 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

4.13 动态绑定 Dynamic Binding: parameterize

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)。 参数化窗体仅调整当前线程中的参数值,以避免与其他线程的竞争条件。

你可能感兴趣的:(Racket)