4.5 Definitions: define
基本定义具有以下形式
(define id expr)
在这种情况下,id 绑定到 expr 的结果。
(define salutation (list-ref '("Hi" "Hello") (random 2))) > salutation "Hello"
4.5.1 Function Shorthand
define 形式还支持函数定义的简写:
(define (id arg ...) body ...+)
这是以下形式的简写
(define id (lambda (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 的函数简写还支持rest 参数(即收集额外参数到列表中的最后一个参数):
(define (id arg ... . rest-id) body ...+)
这是以下形式的简写
(define id (lambda (arg ... . rest-id) body ...+))
(define (avg . l) (/ (apply + l) (length l))) > (avg 1 2 3) 2
4.5.2 Curried Function Shorthand
考虑以下 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 Multiple Values and 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 形式。
(part ("(lib scribblings/reference/reference.scrbl)" "define")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) provides more on definitions.
4.5.4 Internal Definitions
当语法形式的语法指定了 body,则相应的形式可以是定义或表达式。作为 body 的定义是内部定义。
表达式和内部定义可以在 body 序列中混合使用,只要最后一个 body 是表达式。
For example, the syntax of lambda is
(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 (weird) (define x x) x) > (weird) x: undefined;
cannot use before initialization
仅使用 define 的内部定义序列可以很容易地转换为等价的 letrec 形式(如下一节所述)。然而,其他定义形式也可以作为 body 出现,包括 define-values、struct(参见 Programmer-Defined Datatypes)或 define-syntax(参见 Macros)。
(part ("(lib scribblings/reference/reference.scrbl)" "intdef-body")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) documents the fine points of internal definitions.