Mailing List Archive: 49091 messages

## [REBOL] Re: Multi-searches

### From: joel:neely:fedex at: 20-Feb-2002 20:31

```
Hi, Jason,

Jason Cunliffe wrote:
> That's interesting. Could you expand a little please..
> What do you mean by "non-fall-through exit" ?
>

A "fall-through" exit is one that "falls" out of the end of the
function.  A non-fall-through exit terminates the function
evaluation prior to reaching the end of the normal sequential
flow.  RETURN essentially acts as "go to the out door".

Here's a strict one-way-out function to compute the real roots

If the discriminant is...  ...there are _ real roots.
-------------------------  --------------------------
negative                        no
zero                      two equal
positive                   two distinct

8<------------------------------------------------------------
quadform1: func [a [number!] b [number!] c [number!] /local d] [
d: b * b - (4.0 * a * c)
either d < 0 [
[]
][
a: 1.0 / 2.0 / a
b: - b
either d = 0 [
reduce [b * a]
][
d: square-root d
reduce [b + d * a  b - d * a]
]
]
]
8<------------------------------------------------------------

Notice that this "purist" version uses nested conditions to
evaluate only the appropriate expressions.  In contrast:

8<------------------------------------------------------------
quadform2: func [a [number!] b [number!] c [number!] /local d] [
d: b * b - (4.0 * a * c)
if d < 0 [return []]
a: 1.0 / 2.0 / a
b: - b
if d = 0 [return reduce [b * a]]
d: square-root d
reduce [b + d * a  b - d * a]
]
8<------------------------------------------------------------

the above version uses "early exits" via RETURN to bail out as
soon as the result is determined.  YMMV but some folks think
that the second version is easier to read because of the
absence of nesting.

Of course these two are equivalent mod a trivial syntactic
rewrite.  Sometimes early exits make a significant difference
in structure or performance.  Consider a function that tests
whether all numbers in a block are even:

8<------------------------------------------------------------
alleven1: func [blk [block!] /local evensofar] [
evensofar: true
foreach nbr blk [
evensofar: evensofar and even? nbr
]
evensofar
]
8<------------------------------------------------------------

That one is certainly correct, but may work more than necessary
(since FALSE is a zero for AND).  It can be written (at a cost
of performance) to make explicit tests of both the state of
the block and the state of the accumulator variable:

8<------------------------------------------------------------
alleven2: func [blk [block!] /local evensofar] [
evensofar: true
while [all [not empty? blk  evensofar]] [
evensofar: even? first blk
blk: next blk
]
evensofar
]
8<------------------------------------------------------------

...but that's just clunky-looking.  Both of those functions
used fall-through exits, because they were structured to flow
all the way to the end.  If we take off our purist hats and
take advantage of RETURN, we can write this as:

8<------------------------------------------------------------
alleven3: func [blk [block!]] [
foreach nbr blk [
if odd? nbr [return false]
]
true
]
8<------------------------------------------------------------

The RETURN is necessary *inside* the loop to force early
departure from the function.  At the end, we only need to
provide the (trivial) expression for the result.

HTH!

-jn-

--
; sub REBOL {}; sub head (\$) {@_[0]}
REBOL []
# despam: func [e] [replace replace/all e ":" "." "#" "@"]
; sub despam {my (\$e) = @_; \$e =~ tr/:#/.@/; return "\n\$e"}
print head reverse despam "moc:xedef#yleen:leoj" ;
```