[REBOL] Re: a new switch-like flow-control function
From: joel:neely:fedex at: 19-Dec-2003 13:57
Hi, Maxim,
There's more to the story, although YMMV...
Maxim Olivier-Adlhoch wrote:
> describe: func [x [integer!] y [integer!]] [
> switch true compose [
> (x = 1) ["X is one"]
> (y = 1) ["Y is one"]
> (x = 2) ["X is two"]
> (y = 2) ["Y is two"]
> (true) ["X and Y are too big!"]
> ]
> ]
>
> ... (its also 10% faster, on my computer).
>
The timing comparison (even for the trival example I gave) is
*highly* dependent on the range of values which X and Y take!
The issue is that PIF will evaluate only enough guards to find the
first true one, while the SWITCH TRUE COMPOSE approach evaluates
*all* guards (and constructs a new block) every time.
If e.g. your timing test looks like
t0: now/time/precise
loop somebignumber [
for x 1 hi 1 [
for y 1 hi 1 [
describe x y
]
]
]
t1: now/time/precise
print to-decimal t1 - t0
then using HI: 3 will give very different ratios between the two
approaches than HI: 6 or HI: 20 etc.
PIF allows the programmer easily to follow the standard heuristic of
making cheap (or high-probability) tests early to reduce the average
cost of a decision-driven expression, e.g.:
dihedral-signum: func [
x [number!]
y [number!]
z [number!]
][
pif [
x <= 0 [0]
y <= 0 [0]
z <= 0 [0]
10 < square-root abs (
x * (x + 1) * y * (y + 1) * z * (z + 1) -
((x + 2) * (y + 3) * (z + 4))
) [1]
true [-1]
]
]
Also, PIF allows the programmer to use failure of earlier tests as
guards for the evaluation of later tests, which is another useful
(and easy to read/write/understand) technique which the SWITCH TRUE
COMPOSE doesn't support:
fuel-economy: func [
fuel [number!]
distance [number!]
][
pif [
fuel <= 0 ["No data"]
distance / fuel < 20 ["Poor fuel economy!"]
distance / fuel > 40 ["Great fuel economy!"]
true ["Acceptable fuel economy"]
]
]
In the above case, we can only reasonably make the later tests if
the test FUEL <= 0 has failed.
In case anyone is tempted to redesign the tiny examples above, please
remember that those are off-the-cuff examples, and they aren't the
point! The point is that there's inherent economy in only evaluating
enough expressions to get a result (instead of every possible one and
then picking the winner), and there are inherent safety and simplicity
considerations in knowing that a test can only be evaluated if the
previous ones have failed.
ON THE OTHER HAND ...
If I wanted to resurrect the discussion of a non-deterministic choice
among multiple options, I'd be very grateful that you provided a nice
way to express that!!!
nif: func [[throw catch] b [block!] /local options] [
options: copy []
foreach [guard option] compose b [
if guard [append options option]
]
do random/only options
]
e.g. from among the guard/action pairs with true guard, evaluate an
arbitrarily-chosen action, so that
nif [
(x <= y) [x]
(y <= x) [y]
]
nicely expresses that if X and Y are equal, then either one can be
used as the minimum of the two!
Thanks!
-jn-
--
----------------------------------------------------------------------
Joel Neely joelDOTneelyATfedexDOTcom 901-263-4446
Enron Accountingg in a Nutshell: 1c=$0.01=($0.10)**2=(10c)**2=100c=$1