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.