Up till now all functions have had a specified number of named arguments. We will now introduce a syntax for defining variadic functions, which may take a fixed number of named arguments and a variable number of additional arguments which are collected into a named list.
The argument declarations for variadic functions are improper lists:
λ-syntax | Combined DEFINE | |
---|---|---|
3 args |
(LAMBDA (arg1 arg2 arg3) body...)
|
(DEFINE (name arg1 arg2 arg3) body...)
|
≥2 args |
(LAMBDA (arg1 arg2 . rest) body...)
|
(DEFINE (name arg1 arg2 . rest) body...)
|
≥1 args |
(LAMBDA (arg1 . rest) body...)
|
(DEFINE (name arg1 . rest) body...)
|
≥0 args |
(LAMBDA args body...)
|
(DEFINE (name . args) body...)
|
In the definitions above, the parameters are bound as follows:
Definition | (f 1 2 3) |
||
---|---|---|---|
Value of a |
Value of b |
Value of c |
|
(DEFINE (f a b c) body...) |
1 |
2 |
3 |
(DEFINE (f a b . c) body...) |
1 |
2 |
(3) |
(DEFINE (f a . b) body...) |
1 |
(2 3) |
|
(DEFINE (f . a) body...) |
(1 2 3) |
All that is required is a small modification to
make_closure
to accept the declaration:
int make_closure(Atom env, Atom args, Atom body, Atom *result) { Atom p; if (!listp(body)) return Error_Syntax; /* Check argument names are all symbols */ p = args; while (!nilp(p)) { if (p.type == AtomType_Symbol) break; else if (p.type != AtomType_Pair || car(p).type != AtomType_Symbol) return Error_Type; p = cdr(p); } *result = cons(env, cons(args, body)); result->type = AtomType_Closure; return Error_OK; }
And another to apply
to bind the additional arguments
into a list:
int apply(Atom fn, Atom args, Atom *result) { . . . /* Bind the arguments */ while (!nilp(arg_names)) { if (arg_names.type == AtomType_Symbol) { env_set(env, arg_names, args); args = nil; break; } if (nilp(args)) return Error_Args; env_set(env, car(arg_names), car(args)); arg_names = cdr(arg_names); args = cdr(args); } if (!nilp(args)) return Error_Args; . . . }
A boring example:
> ((lambda (a . b) a) 1 2 3) 1 > ((lambda (a . b) b) 1 2 3) (2 3) > ((lambda args args) 1 2 3) (1 2 3)
We can also create a variadic adder:
> (define (sum-list xs) (if xs (+ (car xs) (sum-list (cdr xs))) 0)) SUM-LIST > (sum-list '(1 2 3)) 6 > (define (add . xs) (sum-list xs)) ADD > (add 1 2 3) 6 > (add 1 (- 4 2) (/ 9 3)) 6
Since you can always pass a list to a regular function, this is really just another kind of syntactic sugar.