a new switch-like flow-control function
[1/13] from: antonr::iinet::net::au at: 18-Dec-2003 1:00
I yearn (want), occasionally, a kind of any/switch-like
flow-control function. What I came up with (works ok) is
similar in function to what I want:
do select reduce [
condition-1 [...]
condition-2 [...]
true [...] ; default code
] true
It's kind of like ANY, in that it does the code associated
with the first condition that returns true.
I used it in a feel/detect function like so..
do select reduce [
event/shift [...]
event/control [...]
true [...] ; default
] true
to handle shift-clicks, control-clicks and plain mouse clicks.
But I would like it to be more like ANY.
In ANY, the first evaluated value that is not none is
considered "true" and returned.
In the desired function, the first evaluated condition that
is not none should have its associated code executed.
(That's different to the above - conditions have to evaluate
to true.)
I should use such a function like this:
any-switch [
condition-1 [...]
condition-2 [...]
true []
]
Hmm, maybe it should be a refinement:- switch/any ?
On another note, today I had the occasion to write:
face/parent-face/parent-face/parent-face/parent-face/parent-face
Don't ask me the object hierarchy that leads to that expression. :)
Anton.
[2/13] from: joel:neely:fedex at: 17-Dec-2003 10:19
Hi, Anton,
Set the WayBack machine to 2000... ;-)
Anton Rolls wrote:
> I yearn (want), occasionally, a kind of any/switch-like
> flow-control function...
Some years ago we had a long discussion about generalizing IF on the
list, and kicked around various options/versions. The copy I could
lay my hands on most quickly was Ladislav's refinement (the last in
the discussion???):
pif: func [[throw catch] args [block!] /local res] [
either unset? first res: do/next args [
if not empty? args [
throw make error! [script no-arg pif condition]
]
][
either first res [
either block? first res: do/next second res [
do first res
][
throw make error! [
script expect-arg pif block [block!]
]
]
][
pif second do/next second res
]
]
]
PIF (Polymorphic IF) takes a block of guard-expression/block
pairs and uses the (first true) guard-expressions to select
block to be evaluated. It can be used (I believe) essentially
as you were asking.
Refactoring PIF to iterative form is left as an exercise for
the reader! ;-) As long as the argument block isn't too long
the recursion likely won't be a problem.
Heres a sample usage:
describe: func [x [integer!] y [integer!]] [
pif [
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!"]
]
]
which behaves as:
>> describe 1 1
== "X is one"
>> describe 2 1
== "Y is one"
>> describe 2 2
== "X is two"
>> describe 3 2
== "Y is two"
>> describe 3 3
== "X and Y are too big!"
-jn-
--
----------------------------------------------------------------------
Joel Neely joelDOTneelyATfedexDOTcom 901-263-4446
Enron Accountingg in a Nutshell: 1c=$0.01=($0.10)**2=(10c)**2=100c=$1
[3/13] from: g:santilli:tiscalinet:it at: 18-Dec-2003 10:52
Hi Anton,
On Wednesday, December 17, 2003, 3:00:06 PM, you wrote:
AR> do select reduce [
AR> event/shift [...]
AR> event/control [...]
AR> true [...] ; default
AR> ] true
any [
if event/shift [... true]
if event/control [... true]
(...)
]
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amiga Group Italia sez. L'Aquila --- SOON: http://www.rebol.it/
[4/13] from: antonr:iinet:au at: 19-Dec-2003 1:58
Thanks Joel, I think this is just what I need.
I don't think pif is a good name for it, though, it reminds
me of obscure unix commands, not very rebolish.
But what is a good name?
Perhaps something like "reselect" (because it's a bit
like a select reduce, but since that's not strictly true,
then that could confuse more..) ?
How about "any-select" ? (Guh, same problem)
Here's some ideas:
http://thesaurus.reference.com/search?q=select
- satisfy, select-if, do-elect ...
I don't think there is a good word.
- poly-if <- that is my favourite so far.
Anton.
[5/13] from: maximo:meteorstudios at: 19-Dec-2003 12:10
sorry for being late, but note that this works, straight out of the box:
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!"]
]
]
notice that its as easy to use as pif (its also 10% faster, on my computer).
>> describe 1 1
== "X is one"
>> describe 2 1
== "Y is one"
>> describe 2 2
== "X is two"
>> describe 3 2
== "Y is two"
>> describe 3 3
== "X and Y are too big!"
-Max
[6/13] 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 [
<<quoted lines omitted: 6>>
> ]
> ... (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
[7/13] from: antonr:iinet:au at: 21-Dec-2003 14:35
Actually, I will change it to your suggestion
for this situation, because with only two/three cases
to select from, it is more efficient code.
do select reduce [...] is only better in code-size
for > ~8 cases.
Anton.
[8/13] from: antonr:iinet:au at: 21-Dec-2003 14:49
Oops, should be > 3 cases.
Anton.
[9/13] from: alain:goye:free at: 22-Dec-2003 11:41
Hello all from a newcomer to this list,
Here is a function for only 1 condition but 3 cases : if it can be
useful to anyone
trif: func [
condition
iftrue [block!]
iffalse [block!]
ifnone [block!]
] [
do either condition [
iftrue
] [
either none? condition [
ifnone
] [
iffalse
]
]
]
; example:
print trif request "give an answer" ["yes"]["no"]["cancel"]
by the way,
is it good that "not none" equals "true" ?
Alain Goyé.
[10/13] from: lmecir:mbox:vol:cz at: 23-Dec-2003 16:59
Hi Alain,
>Here is a function for only 1 condition but 3 cases : if it can be
>useful to anyone
<<quoted lines omitted: 5>>
>is it good that "not none" equals "true" ?
>Alain Goyé.
yes, it is useful. The following improvement uses the [throw] attribute
to make the behaviour of the function more standard with respect to Return:
trif: func [
{Three case if}
[throw]
condition
iftrue [block!]
iffalse [block!]
ifnone [block!]
] [
do either condition [
iftrue
] [
either none? condition [
ifnone
] [
iffalse
]
]
]
; example:
f: does [trif request "give an answer" [return "yes"] [return "no"] [return "cancel"]
"Shoudn't get here"]
Merry Christmas
-Ladislav
[11/13] from: greggirwin:mindspring at: 23-Dec-2003 9:21
AG> Hello all from a newcomer to this list,
Welcome Alain!
AG> Here is a function for only 1 condition but 3 cases : if it can be
AG> useful to anyone
AG> trif: func [
AG> condition
AG> iftrue [block!]
AG> iffalse [block!]
AG> ifnone [block!]
For more standard REBOL style, you might consider using hyphens to
separate words. e.g.
if-true [block!]
if-false [block!]
if-none [block!]
You could also use PICK in place of a second either:
trif: func [
condition
if-true [block!]
if-false [block!]
if-none [block!]
] [
do either none? condition
[if-none]
[pick [if-true if-false] condition]
]
Using PICK this way isn't always the clearest choice, but sometimes it
can be quite handy.
AG> by the way,
AG> is it good that "not none" equals "true" ?
I think so. What else would you have it return?
Thanks for posting!
-- Gregg
[12/13] from: patrick:philipot:laposte at: 23-Dec-2003 17:39
Hi List,
What is the meaning of thro?
I have seen it here and there but never quite understood its purpose.
Regards
Patrick
[13/13] from: lmecir:mbox:vol:cz at: 23-Dec-2003 19:21
Hi Pat,
>What is the meaning of thro?
>I have seen it here and there but never quite understood its purpose.
<<quoted lines omitted: 32>>
>>
>[return "cancel"] "Shoudn't get here"]
Try to use the version of the above function without the [throw]
attribute and you will find out, that the result of F will be:
== "Shoudn't get here"
, which indicates, that the Return got "consumed" by Trif , while what
we intended was to "throw" Return to F. The "throw" attribute does
exactly that - throws Return to caller functions.
Merry Christmas
-Ladislav
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted