4.3 Function Calls (Procedure Applications)
形式为
(proc-expr arg-expr ...)
的表达式是一个(function call)函数调用——也称为 procedure application(过程应用)——当 proc-expr 不是绑定为 syntax transformer 的标识符(如 if 或 define)。
4.3.1 Evaluation Order and Arity
函数调用的求值过程是:首先按从左到右的顺序对 proc-expr 和所有 arg-expr 求值。然后,如果 proc-expr 产生的函数 接受与所提供的 arg-expr 数量相同的参数,则调用该函数。 否则,将引发异常。
> (cons 1 null) '(1)
> (+ 1 2 3) 6
> (cons 1 2 3) cons: arity mismatch;
the expected number of arguments does not match the given
number
expected: 2
given: 3
> (1 2 3) application: not a procedure;
expected a procedure that can be applied to arguments
given: 1
某些函数(如 cons)接受固定数量的参数。某些函数(如 + 或 list)接受任意数量的参数。某些函数接受一定范围的参数数量; 例如 substring 接受两个或三个参数。函数的 arity(元数) 是其接受的参数数量。
4.3.2 Keyword Arguments
除了按位置参数外,某些函数还接受 keyword arguments(关键字参数)。 在这种情况下,arg 可以是 arg-keyword arg-expr 序列, 而不仅仅是 arg-expr:
Keywords 介绍了关键字。
(proc-expr arg ...)
arg = arg-expr | arg-keyword arg-expr
例如,
(go "super.rkt" #:mode 'fast)
调用绑定到 go 的函数,其中 "super.rkt" 作为按位置参数, 'fast 作为与 #:mode 关键字关联的参数。关键字隐式地与 其后的表达式配对。
由于单独的关键字不是表达式,因此
(go "super.rkt" #:mode #:fast)
是语法错误。#:mode 关键字后必须跟随一个表达式以产生一个参数值, 而 #:fast 不是表达式。
关键字 arg 的顺序决定了 arg-expr 的求值顺序, 但函数接受关键字参数与它们在参数列表中的位置无关。 上述对 go 的调用也可以等效地写作
(go #:mode 'fast "super.rkt")
(part ("(lib scribblings/reference/reference.scrbl)" "application")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) provides more on 过程应用.
4.3.3 The apply Function
函数调用的语法支持任意数量的参数,但每次具体调用始终指定固定数量的参数。 因此,接受参数列表的函数不能直接将类似 + 的函数应用于列表中的所有项:
(define (avg lst) ; 无法工作... (/ (+ lst) (length lst)))
> (avg '(1 2 3)) +: contract violation
expected: number?
given: '(1 2 3)
(define (avg lst) ; 无法总是工作... (/ (+ (list-ref lst 0) (list-ref lst 1) (list-ref lst 2)) (length lst)))
> (avg '(1 2 3)) 2
> (avg '(1 2)) list-ref: index too large for list
index: 2
in: '(1 2)
apply 函数提供了一种绕过此限制的方法。它接受一个函数和一个 list 参数,并将函数应用于列表中的值:
(define (avg lst) (/ (apply + lst) (length lst)))
> (avg '(1 2 3)) 2
> (avg '(1 2)) 3/2
> (avg '(1 2 3 4)) 5/2
便利的是,apply 函数接受函数与列表之间的额外参数。 这些额外参数会被有效地 cons 到参数列表上:
(define (anti-sum lst) (apply - 0 lst))
> (anti-sum '(1 2 3)) -6
apply 函数也接受关键字参数,并将其传递给被调用的函数:
(apply go #:mode 'fast '("super.rkt")) (apply go '("super.rkt") #:mode 'fast)
包含在 apply 列表参数中的关键字不计入被调用函数的关键字参数; 相反,此列表中的所有参数都被视为按位置参数。要将关键字参数列表传递给函数, 请使 keyword-apply 函数,该函数接受要应用的函数和三个列表。 前两个列表是并行的,其中第一个列表包含关键字(按 keyword<? 排序), 第二个列表包含每个关键字对应的参数。第三个列表包含按位置函数参数, 与 apply 中相同。
(keyword-apply go '(#:mode) '(fast) '("super.rkt"))