Variable arity

A procedure's arity is the number of arguments it takes. You'll probably have noticed that while some of Scheme's built-in procedures always take the same number of arguments (for instance, the arity of the cons procedure is 2, and the arity of the predicate char-uppercase? is 1), others have variable arity -- that is, they can take any number of arguments. All one can say about the arity of a Scheme procedure such as list, +, or string-append is that it is some non-negative integer.

Still other Scheme procedures, such as map and display, require a certain fixed minimum number of arguments, but will accept one or more additional arguments if they are provided. The arity of map is ``2 or more''; the arity of display is ``1 or 2.'' These procedures, too, are said to have variable arity, because their arity varies from one call to another.

It is possible for the programmer to define new variable-arity procedures in Scheme, by using alternate forms of the lambda-expression.

In all of the programmer-defined procedures that we have seen so far, the keyword lambda has been followed by a list of parameters -- names for the values that will be supplied by the caller when the procedure is invoked. If, instead, what follows lambda is a single identifier -- not a list, but a simple identifier, not enclosed in parentheses -- then the procedure denoted by the lambda-expression will accept any number of arguments, and the identifier following lambda will name a list of all the arguments supplied in the call.

Here's a simple example: We'll define a display-line procedure that takes any number of arguments and prints out each one (by applying the display procedure to it), then terminates the output line (by invoking newline). Note that in the lambda-expression, the identifier arguments denotes a list of all the items to be printed:

(define display-line
  (lambda arguments
    (let loop ((rest arguments))
      (if (null? rest)
          (newline)
          (begin
            (display (car rest))
            (loop (cdr rest)))))))

When display-line is invoked, however, the caller does not assemble the items to be printed into a list, but just supplies them as arguments:

> (display-line "+--" "Here is a string!" "--+")
+--Here is a string!--+

> (display-line "ratio = " 35/15)
ratio = 7/3

Exercise 1

Try out some other calls to display-line to check what it prints. For example, try the following:

(display-line "going" "going" "gone")
(display-line "countdown:" 5 4 3 2 1 "done")
(display-line)          ;; apply display-line to no arguments

Explain your results.


Exercise 2

The current version of display-line prints all text together without spaces. Modify the code, so that one space is printed between each value specified for display-line. Thus, the first sample run above should print

+-- Here is a string! --+

Exercise 3

Write a procedure named call-arity that takes any number of arguments and returns the number of arguments it received (ignoring their values):

> (call-arity 'a #\b "c" '(d))
4

> (call-arity 0.0)
1

> (call-arity)
0

If the programmer wishes to require some fixed minimum number of arguments while permitting (but not requiring) additional ones, she can use yet another form of the lambda-expression, in which a dot is placed between the last two identifiers in the parameter list. All the identifiers to the left of this dot correspond to single required arguments. The identifier to the right of the dot designates the list of all of the remaining arguments, the ones that are optional.

For instance, we can define a procedure called display-separated-line that always takes at least one argument, separator, but may take any number of additional arguments. Display-separated-line will print out each of the additional arguments (by invoking display) and terminate the line, just as display-line does, but with the difference that a copy of separator will be displayed between any two of the remaining values. Here is some sample output:

> (display-separated-line "..." "going" "going" "gone")
going...going...gone

> (display-separated-line ":-" 5 4 3 2 1 'done)
5:-4:-3:-2:-1:-done

> (display-separated-line #\space "+--" "Here is a string!" "--+")
+-- Here is a string! --+

> (display-separated-line #\tab 1997 'foo 'wombat 'quux)
1997    foo     wombat  quux

And here is the definition of the procedure:

(define display-separated-line
  (lambda (separator . arguments)
    (if (null? arguments)
        (newline)
        (let loop ((rest arguments))
          (display (car rest))
          (if (null? (cdr rest))
              (newline)
              (begin
                (display separator)
                (loop (cdr rest))))))))

Exercise 4

What happens if you invoke display-separated-line without giving it any arguments? What happens when only one argument is given?


Exercise 5

Revise the display-separated-line procedure so that it displays the separator at the beginning and end of each line, as well as between successive arguments.


The dot notation can be used to specify any number of initial values. Thus, a parameter list of the form

(first-value second-value . remaining-values)

indicates that the first two arguments are required, while additional arguments will be collected into a list named remaining-values.


Exercise 6

Modify the definition of the display-separated-lines procedure so that it takes an output port as its first (required) argument, the separator as its second (required) argument, and any number of values to be printed as additional (optional) arguments. The procedure should write the line, with separators, to the specified output port rather than to the interaction window.


Exercise 7

Write a procedure that takes one or more numbers and computes their arithmetic mean (in other words, their average). For instance, (average 3 5 7 11 14) should return 8. (Note that it would not make sense to invoke this procedure with zero arguments.)


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/courses/scheme/variable-arity.html

created March 22, 1997
last revised March 25, 1998

Henry Walker (walker@math.grin.edu) and John David Stone (stone@math.grin.edu)