4.4 函数(过程):lambda
lambda 表达式用于创建函数。lambda 表达式的最简形式为
(lambda (arg-id ...) body ...+)
带有 n 个 arg-id 的 lambda 形式接受 n 个参数:
> ((lambda (x) x) 1) 1
> ((lambda (x y) (+ x y)) 1 2) 3
> ((lambda (x y) (+ x y)) 1) #<procedure>: arity mismatch;
the expected number of arguments does not match the given
number
expected: 2
given: 1
arguments...:
1
4.4.1 声明剩余参数
lambda 表达式可拥有形式
(lambda rest-id body ...+)
即,lambda 表达式可拥有单个没有括号括住的 rest-id。 其结果函数接受任意数量的参数,这些参数会作为列表绑定到 rest-id。
带有 rest-id 的函数通常使用 apply 来调用另一个接受任意数量参数的函数。
lambda 形式也支持所需参数与 rest-id 结合的形式:
(lambda (arg-id ...+ . rest-id) body ...+)
此形式的结果是一个函数,该函数需要的参数量至少与 arg-id 一样多, 此外它还介绍任意数量的附加参数。
(define max-mag (lambda (num . nums) (apply max (map magnitude (cons num nums))))) > (max-mag 1 -2 0) 2
> (max-mag) max-mag: arity mismatch;
the expected number of arguments does not match the given
number
expected: at least 1
given: 0
变量 rest-id 有时被称作剩余参数,因为它接受函数参数的 “剩余”部分。
4.4.2 声明可选参数
lambda 形式中除剩余参数外的参数不仅可以只有标识符, 还可以有指定了默认值的标识符。
(lambda gen-formals body ...+)
gen-formals = (arg ...) | rest-id | (arg ...+ . rest-id) arg = arg-id | [arg-id default-expr]
形式 [arg-id default-expr] 的参数是可选的。当在一次应用中该参数并未提供时, default-expr 就会产生默认的值。default-expr 中可引用前面的任何 arg-id,而任何之后的 arg-id 也都必须拥有默认值。
(define greet (lambda (given [surname "Smith"]) (string-append "Hello, " given " " surname))) > (greet "John") "Hello, John Smith"
> (greet "John" "Doe") "Hello, John Doe"
(define greet (lambda (given [surname (if (equal? given "John") "Doe" "Smith")]) (string-append "Hello, " given " " surname)))
> (greet "John") "Hello, John Doe"
> (greet "Adam") "Hello, Adam Smith"
4.4.3 声明关键字参数
lambda 形式中可声明按关键字而非位置传入的参数。 关键字参数可与位置固定的参数混合使用,默认值表达式则均支持二者:
关键字参数 中介绍了带关键字的函数调用。
(lambda gen-formals body ...+)
gen-formals = (arg ...) | rest-id | (arg ...+ . rest-id) arg = arg-id | [arg-id default-expr] | arg-keyword arg-id | arg-keyword [arg-id default-expr]
用 arg-keyword arg-id 指定的参数也被使用同样 arg-keyword 的应用所支持。在参数列表中, 成对的关键字-标识符的位置对于应用中参数的匹配来说无关紧要, 因为它会按照关键字而非位置与参数值像匹配。
(define greet (lambda (given #:last surname) (string-append "Hello, " given " " surname)))
> (greet "John" #:last "Smith") "Hello, John Smith"
> (greet #:last "Doe" "John") "Hello, John Doe"
arg-keyword [arg-id default-expr] 参数会根据默认值指定基于关键字的参数。
(define greet (lambda (#:hi [hi "Hello"] given #:last [surname "Smith"]) (string-append hi ", " given " " surname))) > (greet "John") "Hello, John Smith"
> (greet "Karl" #:last "Marx") "Hello, Karl Marx"
> (greet "John" #:hi "Howdy") "Howdy, John Smith"
> (greet "Karl" #:last "Marx" #:hi "Guten Tag") "Guten Tag, Karl Marx"
lambda 形式并不直接支持创建接受“剩余”关键字的函数。 要构造接受所有关键字参数的函数,请使用 make-keyword-procedure。提供给 make-keyword-procedure 的函数通过前两个平行的位置固定参数列表接收关键字参数, 之后该应用中所有位置固定的参数都会作为剩余的位置固定的参数。
apply 函数 介绍了 keyword-apply。
(define (trace-wrap f) (make-keyword-procedure (lambda (kws kw-args . rest) (printf "Called with ~s ~s ~s\n" kws kw-args rest) (keyword-apply f kws kw-args rest)))) > ((trace-wrap greet) "John" #:hi "Howdy") Called with (#:hi) ("Howdy") ("John")
"Howdy, John Smith"
The Racket Reference的Procedure Expressions: lambda and case-lambda一节中提供了关于函数表达式的更多信息。
4.4.4 参数量敏感的函数:case-lambda
case-lambda 形式可创建根据支持的参数量的不同,拥有完全不同行为的函数。 case-lambda 表达式的形式为
(case-lambda [formals body ...+] ...)
formals = (arg-id ...) | rest-id | (arg-id ...+ . rest-id)
其中每个 [formals body ...+] 都类似于 (lambda formals body ...+)。应用由 case-lambda 产生的函数类似于将 lambda 应用到与给定参数量相匹配的第一个分支。
(define greet (case-lambda [(name) (string-append "Hello, " name)] [(given surname) (string-append "Hello, " given " " surname)])) > (greet "John") "Hello, John"
> (greet "John" "Smith") "Hello, John Smith"
> (greet) greet: arity mismatch;
the expected number of arguments does not match the given
number
given: 0
case-lambda 函数无法直接支持可选参数或关键字参数。