一个基本定义具为如下表:
(define id expr)
在这种情况下,id被绑定到expr的结果。
Examples:
(define salutation (list-ref '("Hi" "Hello") (random 2))) > salutation "Hi"
define表还支持函数定义的一个简写:
(define (id arg ...) body ...+)
这是以下内容的简写:
(define id (lambda (arg ...) body ...+))
Examples:
(define (greet name) (string-append salutation ", " name)) > (greet "John") "Hi, John"
(define (greet first [surname "Smith"] #:hi [hi salutation]) (string-append hi ", " first " " surname))
> (greet "John") "Hi, John Smith"
> (greet "John" #:hi "Hey") "Hey, John Smith"
> (greet "John" "Doe") "Hi, John Doe"
函数简写通过define也支持一个剩余参数(rest argument)(即,一个最终参数以在一个列表中收集额外参数):
(define (id arg ... . rest-id) body ...+)
它是以下内容的一个简写:
(define id (lambda (arg ... . rest-id) body ...+))
Examples:
(define (avg . l) (/ (apply + l) (length l))) > (avg 1 2 3) 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是一个函数,接受两个参数,但一次只接受一个参数。一个函数,它接受它的参数的一些并返回一个函数以接受更多,这种函数有时被称为一个柯里函数(curried function)。
使用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对应。
一个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的数量相匹配。
Examples:
(define-values (given surname) (split-name "Adam Smith")) > given "Adam"
> surname "Smith"
一个define表(不是一个函数简写)等价于一个带有一个单个id的define-values表。
在《Racket参考》中的“(define)”部分提供了更多关于定义的内容。
当一个句法表的语法指定body,那相应的表可以是一个定义或一个表达式。作为一个body的一个定义是一个内部定义(internal definition)。
只要最后一个body是表达式,在一个body序列中的表达式和内部定义可以被混合。
例如, lambda的语法是:
(lambda gen-formals body ...+)
所以下面是语法的有效实例:
(lambda (f) ; 没有定义 (printf "running\n") (f 0)) (lambda (f) ; 一个定义 (define (log-it what) (printf "~a\n" what)) (log-it "running") (f 0) (log-it "done")) (lambda (f n) ; 两个定义 (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序列中的内部定义是相互递归的,也就是说,任何定义都可以引用任何其它定义——只要这个引用在定义发生之前没有实际被求值。如果一个定义被过早引用,一个错误就会发生。
Examples:
(define (weird) (define x x) x) > (weird) x: undefined;
cannot use before initialization
内部定义的一个序列只使用define很容易转换为一个等效的letrec表(如同在下一节中介绍的)。然而,其它的定义表可以表现为一个body,包括define-values、struct(见《程序员定义的数据类型》)或define-syntax(见《宏》)。
在《Racket参考》文档的“(intdef-body)”部分有内部定义更多知识点。