4.5 定义:define
基本定义的形式为
(define id expr)
此时 id 会被绑定到 expr 的结果上。
4.5.1 函数简写
define 形式也支持函数定义的简写:
(define (id arg ...) body ...+)
它简写自
(define (greet name) (string-append salutation ", " name)) > (greet "John") "Hello, John"
(define (greet first [surname "Smith"] #:hi [hi salutation]) (string-append hi ", " first " " surname))
> (greet "John") "Hello, John Smith"
> (greet "John" #:hi "Hey") "Hey, John Smith"
> (greet "John" "Doe") "Hello, John Doe"
通过 define 定义的函数简写也支持剩余参数 (即,最后一个参数将额外的参数收集在一个列表中):
(define (id arg ... . rest-id) body ...+)
它简写自
4.5.2 柯里化函数简写
考虑以下 make-add-suffix 函数,它接受一个字符串并返回另一个接受字符串的函数:
(define make-add-suffix (lambda (s2) (lambda (s) (string-append s s2))))
尽管它并不常见,但 make-add-suffix 的结果可被直接调用,就行这样:
> ((make-add-suffix "!") "hello") "hello!"
某种意义上来说,make-add-suffix 是个接受两个参数的函数, 不过它一次只接受其中的一个。接受一部分参数并返回另一个函数来消耗更多参数的函数, 叫做柯里化函数。
使用 define 的函数简写形式,make-add-suffix 可等价地写作
(define (make-add-suffix s2) (lambda (s) (string-append s s2)))
此简写反映了函数调用 (make-add-suffix "!") 的形状。define 形式进一步还支持定义柯里化函数的简写,该简写反映了嵌套的函数调用:
(define ((make-add-suffix s2) s) (string-append s s2))
> ((make-add-suffix "!") "hello") "hello!"
(define louder (make-add-suffix "!")) (define less-sure (make-add-suffix "?"))
> (less-sure "really") "really?"
> (louder "really") "really!"
define 的函数简写的完整语法如下:
(define (head args) body ...+)
head = id | (head args) args = arg ... | arg ... . rest-id
此简写展开后,定义中的每一个 head 都对应一层嵌套的 lambda 形式,最内层的 head 对应最外层的 lambda。
4.5.3 多值与 define-values
Racket 表达式通常只会产生一个结果,然而某些表达式可以产生多个结果。例如, quotient 和 remainder 二者均会产生一个值,而 quotient/remainder 则会一次产生与前二者结果相同的两个值:
> (quotient 13 3) 4
> (remainder 13 3) 1
> (quotient/remainder 13 3)
4
1
如上所示,REPL 会在单独的行中打印每个值。
多值函数可通过 values 函数来实现,该函数接受任意数量的值并将它们作为结果返回:
> (values 1 2 3)
1
2
3
(define (split-name name) (let ([parts (regexp-split " " name)]) (if (= (length parts) 2) (values (list-ref parts 0) (list-ref parts 1)) (error "not a <first> <last> name"))))
> (split-name "Adam Smith")
"Adam"
"Smith"
define-values 形式可将单个表达式产生的多个结果一次绑定到多个标识符:
(define-values (id ...) expr)
expr 产生的结果数量必须与 id 的数量相匹配。
(define-values (given surname) (split-name "Adam Smith")) > given "Adam"
> surname "Smith"
非函数简写的 define 形式等价于只有一个 id 的 define-values 形式。
The Racket Reference的Definitions: define, define-syntax, ...一节中提供了关于定义的更多信息。
4.5.4 内部定义
当一个语法形式的文法中指定了 body 时,其对应的形式可以是定义或表达式。 作为 body 的定义叫做 内部定义。
body 序列中的表达式和内部定义可以混合使用,只要最后一个 body 是表达式就行。
例如,lambda 的语法为
(lambda gen-formals body ...+)
因此下面是有效的文法实例:
(lambda (f) ; no definitions (printf "running\n") (f 0)) (lambda (f) ; one definition (define (log-it what) (printf "~a\n" what)) (log-it "running") (f 0) (log-it "done")) (lambda (f n) ; two definitions (define (call n) (if (zero? n) (log-it "done") (begin (log-it "running") (f n) (call (- n 1))))) (define (log-it what) (printf "~a\n" what)) (call n))
特定的 body 序列中的内部定义是可以互相递归的,也就是说, 任何定义都可以引用任何其它定义——只要引用在其定义之前不会被实际求值就行。 若定义被引用得太早,就会产生错误。
一系列只使用 define 的内部定义很容易被翻译成等价的 letrec 形式(下一节会介绍)。然而,其它定义形式可作为 body 出现,包括 define-values、struct(见 Programmer-Defined Datatypes)或 define-syntax(见 Macros)。
The Racket Reference的Internal Definitions一节中阐述了内部定义的要点。