On this page:
4.3.1 Evaluation Order and Arity
4.3.2 Keyword Arguments
4.3.3 The apply Function

4.3 Function Calls (Procedure Applications)🔗

形式为

(proc-expr arg-expr ...)

的表达式是一个(function call)函数调用——也称为 procedure application(过程应用)——当 proc-expr 不是绑定为 syntax transformer 的标识符(如 ifdefine)。

4.3.1 Evaluation Order and Arity🔗

函数调用的求值过程是:首先按从左到右的顺序对 proc-expr 和所有 arg-expr 求值。然后,如果 proc-expr 产生的函数 接受与所提供的 arg-expr 数量相同的参数,则调用该函数。 否则,将引发异常。

Examples:
> (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"))