Next: Exception handling
Up: Control structures
Previous: Booleans and branching
Index
In closure.diesel:
The predefined closure abstract class is the abstract
superclass of all closure objects. Methods that dispatch on
closure types (e.g. method f(x@&(int):bool):void {...}
)
actually are dispatching on this object. User-defined objects
can inherit from closure in order to inherit
closure-manipulating functions.
predefined abstract class closure;
Closure types follow the standard contravariant subtyping relationship
among closure types, i.e.:
predefined type &(T1, ..., TN):T isa closure, &(`S1 <= T1, ..., `SN <= TN):`S >= T
The loop function invokes its closure argument endlessly. It
never returns normally. To exit the loop, the closure must do a
non-local return or invoke some closure that does (e.g., using
the exit control structure). All other looping constructs are
built upon this function.
fun loop(c:&():void):none;
while implements a standard while-do loop. E.g.:
while({ i < last }, {
...
i := i.succ;
});
fun while(cond:&():bool, c:&():void):void;
Other while-do and do-until loops are implemented using the
{while,until}[_{true,false}]
functions.
The one-argument while_{true,false}
functions simply evaluate
their argument test until it returns true or false, respectively,
presumably for its side effects. The until_
versions evaluate their
body closure and then the test until the test returns true or
false, as appropriate.
fun while_true(cond:&():bool, c:&():void):void; - same as while
fun while_false(cond:&():bool, c:&():void):void;
fun while(cond:&():bool):void;
fun while_true(cond:&():bool):void; - same as while
fun while_false(cond:&():bool):void;
fun until(c:&():void, cond:&():bool):void;
fun until_true(c:&():void, cond:&():bool):void; - same as until
fun until_false(c:&():void, cond:&():bool):void;
The exit[_value][_continue]
constructs support evaluating a block
of code (the body closure), breaking out of it if the break closure
is evaluated inside body. The _value
versions return a
value. The _continue
versions allows body to be
restarted from the beginning when the continue closure is evaluated.
For example, to execute some code, but perhaps quit early:
exit(&(break:&():none){
...
if(..., { eval(break); }); - skip the rest of the body of exit
...
- fall off bottom
});
This idiom supports breaking out of any sort of looping or non-looping
piece of code: just wrap the thing in an exit or exit_value
control
structure and invoke the break block where the control structure should be
exited.
fun exit(c:&(exit:&():none):void):void;
fun exit_value(c:&(exit:&(`T):none):`T):T;
fun exit_continue(c:&(exit:&():none, continue:&():none):void):void;
fun exit_value_continue(c:&(exit:&(`T):none, continue:&():none):T):T;
The loop[_exit[_value]][_continue]
functions evaluate the body
closure again and again until the break closure is evaluated
inside body. For the _continue
versions, execution of body can
be restarted from the beginning by evaluating the continue
closure. The _value
versions return a value.
To write a simple loop with a break statement:
loop_exit(&(break:&():none){
...
if(..., { eval(break); }); - exit loop conditionally
...
- loop
});
To loop and compute a value:
let result:int := loop_exit_value(&(break:&(int):none){
...
if(..., { eval(break, theResult) }); - exit loop, returning theResult
...
});
If both break and continue are desired for an arbitrary iterating
construct, such as times_do
, two exit calls should be used, as in the
following example. The outer call encloses the iterator and
provides breaking out of the loop, while the inner call encloses
the loop body and provides continuing to the next iteration:
exit(&(break:&():none){
10.times_do(&(i:int){ - for i := 0 to 10-1 do
exit(&(continue:&():none){
...
if(..., break); - break out of loop
...
if(..., continue); - continue the iteration by jumping
- to the end of the loop body
...
});
});
});
Some standard iteration control structures, including do and
do_associations
, support break and continue functionality more
conveniently via do[_associations][_exit][_continue]
alternatives.
For example, the following code iterates through the coll
collection, also allowing the body closure to exit the iteration or
continue the iteration with the next element:
coll.do_exit_continue(&(elem:T, break:&():none, continue:&():none){
...
if(..., break); - break out of loop
...
if(..., continue); - continue the iteration with the next element
...
});
fun loop_exit(c:&(exit:&():none):void):void;
fun loop_exit_value(c:&(exit:&(`T):none):void):T;
fun loop_exit_continue(c:&(exit:&():none, continue:&():none):void
):void;
fun loop_exit_value_continue(c:&(exit:&(`T):none,
continue:&():none):void
):T;
fun loop_continue(c:&(continue:&():none):void):void;
Case statements are supported through the switch, case, and else
functions. The elements of switch's argument collection (usually a
vector literal expression) are evaluated in turn, until one of the
test blocks evaluates to true or the else case is found. Then the
corresponding do block is evaluated and its result returned as the
result of the switch function. (Thus switch is very much like Lisp's
cond.) The switch function dies with a run-time error if none of the
cases matches and there is no else case. To illustrate:
let result:string :=
switch([case({ x < 0 }, { "negative" }),
case({ x = 0 }, { "zero" }),
else( { "positive" })]);
Unfortunately, unlike most Diesel control structures, the switch
construct is not as efficient as the corresponding C version: a
vector object is created and filled in with objects containing real
closures, and a bunch of messages get sent. So you might wish to use
chained if expressions instead of switch expressions in the most
time-critical parts of your program. (Some optimizations
have been implemented that often transform switch statements of
this form into an if-then-else chain, but only if you invoke
unrolled_switch
instead of switch, and object creations still
remain unless debug_support
is disabled.)
class case_pair[T] isa case_pair[`S >= T];
fun case(c:&():bool, s:&():`T):case_pair[T];
fun else(s:&():`T):case_pair[T];
fun switch(t:ordered_collection[case_pair[`T]]):T;
fun unrolled_switch(t:i_vector[case_pair[`T]]):T; - an optimized version for short vector literal args
Next: Exception handling
Up: Control structures
Previous: Booleans and branching
Index
Cecil/Vortex Project