14 The Built-In Macros and Special Operators
Many statements include an optional implicit body, which may contain one or more constintuents separated by semicolons. When an implicit body is executed, the expressions in the implicit body are executed in order (left to right). The values of the implicit body are the values of the last expression. If the optional implicit body is not present or contains no expressions, the return value is #f
.
if ( test ) [consequent ]
{ elseif ( elseif-test ) [elseif-consequent ] }*
[ else [alternate ] ]
end [
if ]
expressionbnf
bodybnf
expressionbnf
bodybnf
bodybnf
<object>
.
if
executes one or more expressions, executing and returning the values of a body following the first test which returns true.
test is the first expression to be executed. If its value is true, if
executes and returns the values of the consequent. If the value of test is false, if
proceeds with the optional elseif-tests and alternate.
First the elseif clauses are tried in order. The first elseif-test is executed. If its value is true, the corresponding elseif-consequent is executed and its values are returned as the value of the if
statement. If its value is false, the next elseif-test is tried. This continues until a true elseif-test is found, or until there are no more elseif clauses.
If the test and all the elseif-tests are false, the alternate is executed and its values are returned as the value of the if
statement. If there is no alternate, the if
statement returns #f
.
if ( x < 0 ) - x; end if; if ( heads?(flip(coin)) ) start(black); else start(white); end if if (player1.money <= 0) end-game(player1) elseif (player2.money <= 0) end-game(player2) else move(player1); move(player2); end if if ( camel.humps = 1 ) "dromedary" elseif ( camel.humps = 2 ) "bactrian" else "not a camel" end if;
unless ( test )
[ body ]
end [ unless ]
expressionbnf
bodybnf
<object>
.
unless
executes test. If the value of test is false, then the body is executed and its values are returned by unless
. If the value of test is true, the body is not executed and unless
returns #f
.
If there are no expressions in the body, then #f
is returned.
unless(detect-gas? (nose)) light(match) end unless
case
{ test => consequent } *
[ otherwise [ => ] alternate ]
end [ case ]
expressionbnf
[ constituentsbnf ] ;
[ constituentsbnf ] ;
<object>
.
As a special case, the name otherwise may appear as a test. This test always succeeds if there is no preceding successful test.
If no test is true, then case returns #f.
case player1.money <= 0 => end-game(player1); player2.money <= 0 => end-game(player2); otherwise => move(player1); move(player2); end case;
select ( target
[ by test ] )
{ matches => consequent }*
[ otherwise [ => ] alternate ]
end [ select ]
expressionbnf
expressionbnf
{ expressionbnf } ,+ | ( { expressionbnf } ,+ )
[ constituentsbnf ] ;
[ constituentsbnf ]
;
<object>
.
select
generates a target object and then compares it to a series of potential matches, in order. If it finds a match, it executes the corresponding consequent and returns the values of the consequent. If no match is found, an error is signaled.The target is executed to produce the match object.
The test, if supplied, is a function used to compare the target object to the potential matches. The default test is ==
.
One at a time, each match is executed and its value compared to target, in order. If a match is found, the corresponding consequent is executed and its values are returned. If the corresponding consequent is empty, #f
is returned.
Once a match is found, subsequent matches and the corresponding bodies are not executed.
As a special case, the name otherwise
may appear instead of a matches. This will be considered a match if no other match is found.
If there is no matching clause, an error is signaled. Because an otherwise
clause matches when no other clause matches, a select
form that includes an otherwise
clause will never signal an error for failure to match.
Since testing stops when the first match is found, it is irrelevant whether the test function would also have returned true if called on later matches of the same clause or on matches of later clauses.
select ( career-choice(student) ) art:, music:, drama: => "Don't quit your day job"; literature:, history:, linguistics: => "That really is fascinating"; science:, math:, engineering: => "Say, can you fix my VCR?"; otherwise => "I wish you luck"; end select; select ( my-object by instance? ) <window>, <view>, <rectangle> => "a graphical object"; <number>, <string>, <list> => "a computational object"; otherwise => "I don't know"; end select
while ( test )
[ body
]
end
[ while ]
Þ #f
expressionbnf
bodybnf
#f
while
loops over body until test returns false.
Each pass through the loop begins by executing test. If test returns a true value, the expressions in the body are executed and the looping continues. If test returns false, the loop terminates and while
returns #f
.
until ( test )
[ body
]
end
[until
] #f
expressionbnf
bodybnf
#f
until
loops over body until test returns true.
Each pass through the loop begins by executing test. If test returns false, the expressions in the body are executed and the looping continues. If test returns true, the loop terminates and until
returns #f
.
for ( { for-clause } ,* |
{ { for-clause ,}* end-clause })
[ loop-body ]
[ finally [ result-body ] ]
end [ for ]
|
|
expressionbnf
bodybnf
bodybnf
variablebnf = init-value then next-value
variablebnf in collection
variablebnf from start
[ { to | above |below } bound ]
[ by increment ]
{ until: | while: } end-test
expressionbnf
expressionbnf
expressionbnf
expressionbnf
expressionbnf
expressionbnf
<object>
.
for
iterates over loop-body, creating and updating iteration bindings on each iteration according to the for-clauses. Iteration ends when one of the for-clauses is exhausted, or when the optional end-test is satisfied.Each for-clause controls one iteration binding. The optional end-test does not control any iteration bindings.
There are three kinds of for-clauses: explicit-step-clauses, collection-clauses, and numeric-clauses: An explicit-step-clause creates bindings for the results of executing an expression. A collection-clause creates bindings for successive elements of a collection. A numeric-clause creates bindings for a series of numbers.
Execution of a for
statement proceeds through the following steps:
Execute the expressions that are executed just once, in left to right order as they appear in the for
statement. These expressions include the types of all the bindings, and the expressions init-value, collection, start, bound, and increment. If the value of collection is not a collection, an error is signaled. The default value for increment is 1
.
Create the iteration bindings of explicit step and numeric clauses.
Check numeric and collection clauses for exhaustion. If a clause is exhausted, go to step 9.
above
is specified, the clause will be in bounds as long as the value is greater than the bounds. If below
is specified, the clause will be in bounds as long as the value is less than the bounds. If to
is specified with a positive or zero increment, the clause will be in bounds as long as it is less than or equal to the bounds. If to
is specified with a negative increment, the clause will be in bounds as long as it is greater than or equal to the bounds.For each collection clause create the iteration binding for the next element of the collection for that clause. Fresh bindings are created each time through the loop (i.e., the binding is not assigned the new value). If the binding is typed and the value is not of the specified type, signal an error.
If end-test is supplied, execute it. If the value of end-test is false and the symbol is while:
, go to step 9. If the value of end-test is true and the symbol is until:
, go to step 9.
Execute the expressions in the body in order. The expressions in the body are used to produce side-effects.
Obtain the next values for explicit step and numeric clauses. Values are obtained in left to right order, in the environment produced by step 6.
+
.Create the iteration bindings of explicit step and numeric clauses for the values obtained in step 7. For each clause, if a binding type is supplied and the next value for that clause is not of the specified type, signal an error. Fresh bindings are created each time through the loop (i.e., the binding is not assigned the new value). After the bindings have been created, go to step 3.
Execute the expressions in the result-body in order. Bindings created in step 2 and 8 are visible during the execution of result-body, but bindings created in step 4 ( the iteration bindings of collection clauses) are not visible during the execution of result-body. The values of the last expression in the result-body are returned as the values of the for
statement. If there are no expressions in the result-body, for
returns #f
.
for ( thing = first-thing then next(thing), until: done?(thing) ) do-some(thing) end; for (j :: <integer> from 0 to height) for (i :: <integer> from 0 to width) erase(i,j); plot (i,j); end for; end for; for (city in olympic-cities, year from start-year by 4) schedule-olympic-game(city, year) finally: notify(press); sell(tickets); end; for (i from 0 below 100, zombies from 0 below 100, normals from 100 above 0 by -1) population[i] := zombies + normals end;
begin [ body ] end
bodybnf
<object>
.
#f
is returned.
block ( [ exit-variable ] )
[ block-body ]
[ afterwards [ afterwards-clause ] ]
[ cleanup [ cleanup-clause ] ]
{ exception exception-clause }*
end [ block ]
variable-namebnf
bodybnf
bodybnf
bodybnf
( [ name :: ] type { ,exception-options }*)
[ bodybnf ]
variable-namebnf
expressionbnf
{ test: expressionbnf } | { init-arguments: expressionbnf }
<object>
.
block
executes the expressions in the block-body in order, and then the executes the optional afterwards-clause and cleanup-clause. Unless there is a non-local exit, block returns the values of the block-body, or #f
if there is no block-body.
If exit-variable is provided, it is bound to an exit procedure (an object of type <function>
) which is valid during the execution of the block body and the clauses. At any point in time before the last clause returns, the exit procedure can be called. Calling the exit procedure has the effect of immediately terminating the execution of the block, and returning as values the arguments to the exit procedure.
The body of the afterwards-clause, if provided, is executed after the block-body. The values produced by the afterwards-clause are ignored.
The body of the cleanup-clause, if provided, is executed after the block-body and afterwards-clause. Its values are also ignored. The cleanup clause differs from the afterwards clause in that its body is guaranteed to be executed, even if the execution of the block is interrupted by a non-local exit. There is no such guarantee for the afterwards clauses.
For example, the following code fragment ensures that files are closed even in the case of an error causing a non-local exit from the block body:
block (return) open-files(); if (something-wrong) return("didn't work"); end if; compute-with-files() cleanup close-files(); end blockThe exception-clauses, if supplied, install exception handlers during the execution of the block-body, afterwards-clause, and cleanup-clause. If one of these handlers is invoked, it never declines but immediately takes a non-local exit to the beginning of the block, executes the expressions in its body and returns the values of the last expression or
#f
if the body is empty. Note that when the expressions in an exception body are executed, all handlers established by the block
are no longer active. Note also that the cleanup clause of the block will be executed before the expressions of the handler body are executed.
The type and exception-options are as for let handler
. If present, name is bound to the condition during the execution of the handler's body.
The exception clauses are checked in the order in which they appear. That is, the first handler will take precedence over the second, the second over the third, etc.
The following is a trivial use of an exception clause.
block (return) open-files(); compute-with-files() exception (<error>) "didn't work") cleanup close-files(); end block
Note that a block statement may also be exited due to the execution of a handler clause. Before the exception clause is executed, intervening cleanup clauses are executed as described above (including any clause for the establishing block.) The exit procedure may be invoked during execution of exception clauses, in which case the argument values are immediately returned from the block (the cleanup clause already having been executed).
During the process of executing the cleanup clauses of the intervening blocks, any valid exit procedure may be invoked and may interrupt the current non-local exit.
All exception clauses are executed in the same dynamic environment. None of the handlers established in the block are visible during the execution of one of the handlers. This can be thought of as parallel installation of the handlers.
In the following example, the block
establishes an exit procedure in the binding bar
. The block
returns a method containing a call to bar
, and the method is stored in the binding foo
. Calling foo
is an error because it is no longer valid to invoke bar
after its establishing block
has returned.
define constant foo = block (bar) method (n) bar(n) end; end block; foo(5) {error or other undefined consequences}
method
[Statement]
method parameter-list [ body ] end [ method ]
parameter-listbnf
bodybnf
<method>
.
method
creates and returns a method specified by the parameter-list and body. For a complete description of methods, see "Methods" on page 78.
Generated with Harlequin WebMaker