Control structures

This chapter describes the (rnrs control (6))library, which provides useful control structures.

(when <test> <expression1> <expression2> ...)    syntax 
(unless <test> <expression1> <expression2> ...)    syntax 

Syntax: <Test> must be an expression.

Semantics: A when expression is evaluated by evaluating the <test> expression. If <test> evaluates to a true value, the remaining <expression>s are evaluated in order, and the results of the last <expression> are returned as the results of the entire when expression. Otherwise, the when expression returns unspecified values. An unless expression is evaluated by evaluating the <test> expression. If <test> evaluates to #f, the remaining <expression>s are evaluated in order, and the results of the last <expression> are returned as the results of the entire unless expression. Otherwise, the unless expression returns unspecified values.

The final <expression> is in tail context if the when or unless form is itself in tail context.

(when (> 3 2) ’greater)         ⇒ greater
(when (< 3 2) ’greater)         ⇒ unspecified
(unless (> 3 2) ’less)         ⇒ unspecified
(unless (< 3 2) ’less)         ⇒ less

The when and unless expressions are derived forms. They could be defined by the following macros:

(define-syntax when
  (syntax-rules ()
    ((when test result1 result2 ...)
     (if test
         (begin result1 result2 ...)))))

(define-syntax unless
  (syntax-rules ()
    ((unless test result1 result2 ...)
     (if (not test)
         (begin result1 result2 ...)))))

(do ((<variable1> <init1> <step1>)    syntax 
...)
(<test> <expression> ...)
<command> ...)

Syntax: The <init>s, <step>s, <test>s, and <command>s must be expressions. The <variable>s must be pairwise distinct variables.

Semantics: The do expression is an iteration construct. It specifies a set of variables to be bound, how they are to be initialized at the start, and how they are to be updated on each iteration.

A do expression is evaluated as follows: The <init> expressions are evaluated (in some unspecified order), the <variable>s are bound to fresh locations, the results of the <init> expressions are stored in the bindings of the <variable>s, and then the iteration phase begins.

Each iteration begins by evaluating <test>; if the result is #f, then the <command>s are evaluated in order for effect, the <step> expressions are evaluated in some unspecified order, the <variable>s are bound to fresh locations holding the results, and the next iteration begins.

If <test> evaluates to a true value, the <expression>s are evaluated from left to right and the values of the last <expression> are returned. If no <expression>s are present, then the do expression returns unspecified values.

The regionof the binding of a <variable> consists of the entire do expression except for the <init>s.

A <step> may be omitted, in which case the effect is the same as if (<variable> <init> <variable>) had been written instead of (<variable> <init>).

If a do expression appears in a tail context, the <expression>s are a <tail sequence> in the sense of report section on “Tail calls and tail contexts”, i.e., the last <expression> is also in a tail context.

(do ((vec (make-vector 5))
     (i 0 (+ i 1)))
    ((= i 5) vec)
  (vector-set! vec i i))                  ⇒  #(0 1 2 3 4)

(let ((x ’(1 3 5 7 9)))
  (do ((x x (cdr x))
       (sum 0 (+ sum (car x))))
      ((null? x) sum)))                     ⇒  25

The following definition of do uses a trick to expand the variable clauses.

(define-syntax do
  (syntax-rules ()
    ((do ((var init step ...) ...)
         (test expr ...)
         command ...)
     (letrec
       ((loop
         (lambda (var ...)
           (if test
               (begin
                 #f ; avoid empty begin
                 expr ...)
               (begin
                 command
                 ...
                 (loop (do "step" var step ...)
                       ...))))))
       (loop init ...)))
    ((do "step" x)
     x)
    ((do "step" x y)
     y)))

(case-lambda <case-lambda clause> ...)    syntax 

Syntax: Each <case-lambda clause> must be of the form

(<formals> <body>)

<Formals> must be as in a lambda form (report section on “Procedures”), and <body> is as described in report section on “Bodies and sequences”.

Semantics: A case-lambda expression evaluates to a procedure. This procedure, when applied, tries to match its arguments to the <case-lambda clause>s in order. The arguments match a clause if one of the following conditions is fulfilled:

For the first clause matched by the arguments, the variables of the <formals> are bound to fresh locations containing the argument values in the same arrangement as with lambda.

The last expression of a <body> in a case-lambda expression is in tail context.

If the arguments match none of the clauses, an exception with condition type &assertion is raised.

(define foo
  (case-lambda 
   (() ’zero)
   ((x) (list ’one x))
   ((x y) (list ’two x y))
   ((a b c d . e) (list ’four a b c d e))
   (rest (list ’rest rest))))

(foo)         ⇒ zero
(foo 1)         ⇒ (one 1)
(foo 1 2)         ⇒ (two 1 2)
(foo 1 2 3)         ⇒ (rest (1 2 3))
(foo 1 2 3 4)         ⇒ (four 1 2 3 4 ())

The case-lambda keyword can be defined in terms of lambda by the following macros:

(define-syntax case-lambda
  (syntax-rules ()
    ((_ (fmls b1 b2 ...))
     (lambda fmls b1 b2 ...))
    ((_ (fmls b1 b2 ...) ...)
     (lambda args
       (let ((n (length args)))
         (case-lambda-help args n
           (fmls b1 b2 ...) ...))))))

(define-syntax case-lambda-help
  (syntax-rules ()
    ((_ args n)
     (assertion-violation #f
       "unexpected number of arguments"))
    ((_ args n ((x ...) b1 b2 ...) more ...)
     (if (= n (length ’(x ...)))
         (apply (lambda (x ...) b1 b2 ...) args)
         (case-lambda-help args n more ...)))
    ((_ args n ((x1 x2 ... . r) b1 b2 ...) more ...)
     (if (>= n (length ’(x1 x2 ...)))
         (apply (lambda (x1 x2 ... . r) b1 b2 ...)
                   args)
         (case-lambda-help args n more ...)))
    ((_ args n (r b1 b2 ...) more ...)
     (apply (lambda r b1 b2 ...) args))))