Next: , Previous: , Up: stdlib   [Index]


5.5 Control structures

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

Syntax: when ?test ?expression1 ?expression2
Syntax: unless ?test ?expression1 ?expression2

?test must be an expression.

A when expression is evaluated by evaluating the ?test expression. Only if ?test evaluates to a true value, the remaining ?expressions are evaluated in order. A syntax use of when always returns zero values.

NOTE This breaks R6RS compliance. According to the standard: if ?test evaluates to a true value: 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. Only if ?test evaluates to #f, the remaining ?expressions are evaluated in order. A syntax use of unless always returns zero values.

NOTE This breaks R6RS compliance. According to the standard: if ?test evaluates to a true value: the results of the last ?expression are returned as the results of the entire unless expression; otherwise, the unless expression returns unspecified values.

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 ...)))))
Syntax: do ((?variable1 ?init1 ?step1) …) (?test ?expression …) ?command

The ?inits, ?steps, ?tests, and ?commands must be expressions. The ?variables must be pairwise distinct variables.

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 ?variables are bound to fresh locations, the results of the ?init expressions are stored in the bindings of the ?variables, and then the iteration phase begins.

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

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

The region consists of the entire do expression except for the ?inits.

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 ?expressions are a ?tail sequence in the sense of report section baselib tail call, 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)))
Syntax: case-lambda ?case-lambda-clause

Each ?case-lambda-clause must be of the form:

(?formals ?body)

?formals must be as in a lambda form (baselib expressions procedures), and ?body is as described in report section baselib bodies.

A case-lambda expression evaluates to a procedure. This procedure, when applied, tries to match its arguments to the ?case-lambda-clauses 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))))

Next: , Previous: , Up: stdlib   [Index]