Switch with refinements/hmmm!
[1/25] from: tim:johnsons-web at: 31-Oct-2001 8:16
Hello:
It sure would be nice if I could use 'switch with refinements.
I can't seem to wrap my poor little brain around that one.
Any ideas?
Thanks
--
Tim Johnson <[tim--johnsons-web--com]>
http://www.johnsons-web.com
[2/25] from: joel:neely:fedex at: 31-Oct-2001 15:03
Hi, Tim,
Tim Johnson wrote:
> Hello:
> It sure would be nice if I could use 'switch with refinements.
> I can't seem to wrap my poor little brain around that one.
> Any ideas?
>
Could you supply a hypothetical sample of code that would illustrate
what you have in mind?
-jn-
--
This sentence contradicts itself -- no actually it doesn't.
-- Doug Hofstadter
joel<dot>neely<at>fedex<dot>com
[3/25] from: tim:johnsons-web at: 31-Oct-2001 12:39
On Wed, Oct 31, 2001 at 03:03:03PM -0600, Joel Neely wrote:
> Hi, Tim,
> Tim Johnson wrote:
<<quoted lines omitted: 6>>
> Could you supply a hypothetical sample of code that would illustrate
> what you have in mind?
Hi Joel!
refine-switch: func[ /ref-one /ref-two /ref-three][
switch <duh>what would I put her</duh>[ ; needs a value
ref-one[print "ref-one"]
ref-two[print "ref-two"]
ref-three[print "ref-three"]
]
]
Thanks Joel :>)
[4/25] from: greggirwin:mindspring at: 31-Oct-2001 15:01
Hi Tim,
<< It sure would be nice if I could use 'switch with refinements.
I can't seem to wrap my poor little brain around that one.
Any ideas? >>
I'm not clear on what you're not clear on. :) If you mean using the /default
refinement, I understand what you mean. That threw me for a loop as well. If
that's the case, maybe this will help. The default case is a block unto
itself, after the block of defined switch cases.
REBOL []
test-switch: does [
prin [ref #"^-"]
switch/default type?/word ref [
object! [print "!O"]
error! [print "!E"]
word! [print "!W"]
any-function! [print "!F"]
none! [print "!-"]
block! [print "!B"]
string! [print "!S"]
integer! [print "!I"]
decimal! [print "!D"]
] [print to-string type? ref]
]
ref: "A" test-switch
ref: 1 test-switch
ref: 1.1 test-switch
ref: none test-switch
halt
--Gregg
[5/25] from: tim:johnsons-web at: 31-Oct-2001 13:12
On Wed, Oct 31, 2001 at 03:01:09PM -0700, Gregg Irwin wrote:
> Hi Tim,
>
> << It sure would be nice if I could use 'switch with refinements.
> I can't seem to wrap my poor little brain around that one.
> Any ideas? >>
>
Hi Gregg
> I'm not clear on what you're not clear on. :)
And I don't blame you because I wasn't clear on what I wasn't clear on.
I've posted this response to Joel's response: Really meant to include
using 'switch in a function
refine-switch: func[ /ref-one /ref-two /ref-three][
switch <duh>what would I put her</duh>[ ; needs a value
ref-one[print "ref-one"]
ref-two[print "ref-two"]
ref-three[print "ref-three"]
]
]
; hopefully that makes my lack of clarity clearer. :>)
tj
[6/25] from: lmecir:mbox:vol:cz at: 31-Oct-2001 23:27
Hi Tim,
...
> refine-switch: func[ /ref-one /ref-two /ref-three][
> switch <duh>what would I put her</duh>[ ; needs a value
<<quoted lines omitted: 5>>
> Thanks Joel :>)
> > -jn-
how about:
refine-switch: func[ /ref-one /ref-two /ref-three][
pif [
ref-one [print "ref-one"]
ref-two [print "ref-two"]
ref-three [print "ref-three"]
]
]
You can find PIF in http://www.sweb.cz/LMecir/highfun.r
Cheers
Ladislav
[7/25] from: sterling::rebol::com at: 31-Oct-2001 14:23
Well, if you only expect one of the refinements to be used...
refine-switch: func[ /ref-one /ref-two /ref-three][
switch true reduce [ ; needs a value
ref-one [print "ref-one"]
ref-two [print "ref-two"]
ref-three [print "ref-three"]
]
]
will catch the first one that is set. You have a new problem if more
than one can set used.
Sterling
[8/25] from: joel:neely:fedex at: 31-Oct-2001 17:10
Hi, Tim,
Tim Johnson wrote:
> refine-switch: func[ /ref-one /ref-two /ref-three][
> switch <duh>what would I put her</duh>[ ; needs a value
<<quoted lines omitted: 3>>
> ]
> ]
If you expect at most one refinement to be used
refine-switch: func [ /refa /refb /refc ] [
either refa [
print "ref A"
][either refb [
print "ref B"
][either refc [
print "ref C"
][
print "NO ref"
]]]
]
If more than one can be present (which, of course, REBOL syntax
allows to occur)
refine-switch: func [/refa /refb /refc][
either any [refa refb refc] [
if refa [print "ref A"]
if refb [print "ref B"]
if refc [print "ref C"]
][
print "NO ref"
]
exit
]
(or several variations!)
I know I didn't answer exactly the way the question was worded,
but I'm not sure that SWITCH is the most obvious solution.
Sterling's reply was ingeneous (and I've certainly done such
things myself ;-) but it's not likely to be immediately clear to
the casual reader what's going on .
HTH!
-jn-
--
This sentence contradicts itself -- no actually it doesn't.
-- Doug Hofstadter
joel<dot>neely<at>fedex<dot>com
[9/25] from: tim:johnsons-web at: 31-Oct-2001 14:38
Hello All:
Thanks to all. Will look at both Sterling and Ladislav's
examples.
Regards
Tim
[10/25] from: dockimbel:free at: 1-Nov-2001 2:38
If you need to set several refinement at the same time, here's my variant :
refine-switch: func [/ref-one /ref-two /ref-three][
all [ref-one print "ref-one"]
all [ref-two print "ref-two"]
all [ref-three print "ref-three"]
]
-DocKimbel
[sterling--rebol--com] wrote:
[11/25] from: joel:neely:fedex at: 31-Oct-2001 21:01
Hi, Tim and all...
Just to recap the interesting variety of responses, there are multiple
design decisions to be addressed, depending on the minimum and maximum
number of switches/refinements that may be set:
0) Is none an option?
If so, is there to be a default behavior?
If not, what (if any) error checking/handling is to be performed if
none is set?
1) Is one the only option?
If so, what (if any) error checking/handling is to be performed if
multiple refinements are set?
n) Is more than one an option?
If not (... error etc. as above...)?
If so which action(s) is/are to be performed?
The first one selected in textual order?
All that are selected?
Exactly one, randomly chosen?
Nenad Rakocevic wrote:
> If you need to set several refinement at the same time, here's my variant :
>
> refine-switch: func [/ref-one /ref-two /ref-three][
> all [ref-one print "ref-one"]
> all [ref-two print "ref-two"]
> all [ref-three print "ref-three"]
> ]
>
Nice, and very REBOL-flavored, but only works under some circumstances:
refine-switch: func [trace [logic!] /refa /refb /refc /local count] [
count: 0
all [refa if trace [print "ref A"] count: count + 1]
all [refb if trace [print "ref B"] count: count + 2]
all [refc if trace [print "ref C"] count: count + 4]
count
]
Note the presence of an embedded expression that can evaluate to NONE,
which aborts each ALL statement prematurely, as in
>> refine-switch false
== 0
>> refine-switch/refa false
== 0
>> refine-switch/refa true
ref A
== 1
>> refine-switch/refa/refb true
ref A
ref B
== 3
>> refine-switch/refa/refb false
== 0
Using ALL this way depends on all "brances" being made up of expressions
that will never return NONE or FALSE. Of course, if that's the case for
your particular need, then it'll work just fine.
-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" ;
[12/25] from: media:quazart at: 1-Nov-2001 7:56
> If you expect at most one refinement to be used
> refine-switch: func [ /refa /refb /refc ] [
<<quoted lines omitted: 8>>
> ]]]
> ]
I just thought I'd share some knowledge... The above can also be coded like
so:
refine-switch: func [ /refa /refb /refc ] [
any[
all [refa print "ref A"]
all [refb print "ref b"]
all [refc print "ref c"]
print "NO ref"
]
]
this sort of fixes doc kimbel's approach IMHO
here, the first reference to be set will be returned...
you could also code the following:
refine-switch: func [ /refa /refb /refc ] [
any[
all [refa refb refc print "all refs are set !"]
all [refa refb print "ref a & b"]
all [refa refc print "ref a & c"]
all [refb refc print "ref b & c"]
all [refa print "ref a"]
all [refb print "ref b"]
all [refc print "ref c"]
print "NO ref"
]
]
this approach actually handles all cases... either all, two or only one of
the refs may be set, and you can react differently to each different case.
the nice thing about the any and all combo is that its VERY easy to create
large or complex value filters. the syntax so simple, it doesn't make such
a bad program if you insert the values by hand like I did above... doing
that in IF/ELSE would have been a MAJOR pain... just try it you'll see.
its also very to debug like I did above.
my two cents.
[13/25] from: nitsch-lists:netcologne at: 1-Nov-2001 18:37
RE: [REBOL] Re: Switch with refinements/hmmm!
or with a macro:
[REBOL [title: "refine-select"]
refine-select: func [block /default if-nothing /local todo] [
todo: clear []
foreach [word then-do] block [
if get word [append todo then-do]
]
if empty? todo [append todo if-nothing]
copy todo
]
test: func [/a /b /local todo] [
todo: refine-select/default [
a [print "is a"]
b [print "is b"]
] [print "nothing to do"]
? todo
do todo
]
test/a/b
test
[--result:
TODO is a block of value: [print "is a" print "is b"]
is a
is b
TODO is a block of value: [print "nothing to do"]
nothing to do
]
]
-Volker
[dockimbel--free--fr] wrote:
[14/25] from: joel:neely:fedex at: 1-Nov-2001 14:10
Hi, Media,
(Is that your real name, or is it short for Mediator? ;-)
Media wrote:
> > If you expect at most one refinement to be used
> >
<<quoted lines omitted: 20>>
> ]
> this sort of fixes doc kimbel's approach IMHO
I'm afraid it doesn't. If an ALL block contains an expression
which evaluates to NONE or FALSE, then that block is short-
circuit exited at that point. A trivial example is
all [optionflag locword: none otherstuff]
in which OTHERSTUFF never gets evaluated. This could be "fixed"
by rewrapping the rest of the block after OPTIONFLAG as
all [optionflag do [locword: none otherstuff]]
but that's just obfuscation IMHO, if what we're really trying
to say is
if optionflag [locword: none otherstuff]
> you could also code the following:
> refine-switch: func [ /refa /refb /refc ] [
<<quoted lines omitted: 12>>
> or only one of the refs may be set, and you can react
> differently to each different case.
Hmmmm... Two thoughts:
1) This has the same short-circuit-exiting issue as described
above.
2) This creates a combinatorial explosion as more refinements
are added. Adding /REFD to the mix *doubles* the size of
the code, as opposed to
refcombos: func [/refa /refb /refc /local r n limit check] [
r: copy [] n: 0 limit: 3
check: func [opt label] [
if opt [repend r [pick ["&" ""] (n: n + 1) > 1 label]]
]
check refa "A"
check refb "B"
check refc "C"
print switch/default n reduce [
0 ["NO refs"]
limit ["all refs are set !"]
][join "ref " form r]
]
which scales up linearly with an increasing number of options.
> the nice thing about the any and all combo is that its VERY
> easy to create large or complex value filters. the syntax
> so simple, it doesn't make such a bad program if you insert
> the values by hand like I did above... doing that in IF/ELSE
> would have been a MAJOR pain... just try it you'll see.
>
... but notice also that it can require LOTS of redundant
evaluation of the same refinements. Compared with
reftree: func [/refa /refb /refc] [
either refa
[either refb
[either refc
[print "all refs are set!"]
[print "ref A & B"]]
[either refc
[print "ref A & C"]
[print "ref A"]]]
[either refb
[either refc
[print "ref B & C"]
[print "ref B"]]
[either refc
[print "ref C"]
[print "NO refs"]]]
]
(which only evaluates each refinement once) it looks like a classic
case of "pay me now or pay me later". We either spend programmer
time to craft a faster process or we spend the cycles at run time.
-jn-
--
This sentence contradicts itself -- no actually it doesn't.
-- Doug Hofstadter
joel<dot>neely<at>fedex<dot>com
[15/25] from: rotenca:telvia:it at: 2-Nov-2001 1:20
Hi, Joel Neely
> reftree: func [/refa /refb /refc] [
> either refa
<<quoted lines omitted: 16>>
> case of "pay me now or pay me later". We either spend programmer
> time to craft a faster process or we spend the cycles at run time.
Why not:
v: to-string replace/all reduce [ all[refa "a"] all [refb "b"] all [refc "c"]]
none ""
print select ["" "no refs" "a" "refa" "b" "refb" "ab" "refa & b" "c" "refc"
ac
"refa & refc" "bc" "refb & refc" "abc" "refa & refb & refc"] v
I find it more rebolesque. It is easy to mantain. It is fast. It is not
C-like. It can be also more simple, but less readable:
print select [[none none none] "no refs" [true none none] "a"...] reduce [
refa refb refc ]
> -jn-
---
Ciao
Romano
[16/25] from: joel:neely:fedex at: 1-Nov-2001 19:44
Hi, Romano,
Yet another interesting specimen for our growing menagerie!
Romano Paolo Tenca wrote:
> Hi, Joel Neely
> > reftree: func [/refa /refb /refc] [
<<quoted lines omitted: 3>>
> > [print "all refs are set!"]
> > [print "ref A & B"]]
...
> > ]
> >
<<quoted lines omitted: 6>>
> print select ["" "no refs" "a" "refa" "b" "refb" "ab" "refa & b" "c" "refc"
> "ac" "refa & refc" "bc" "refb & refc" "abc" "refa & refb & refc"] v
Well (first putting it into a function for apples-to-apples comparison, and
eliminating the superfluous variable ...) it seems clear to me that we have
yet another tradeoff in this version:
ref-sw: func [/refa /refb /refc] [
print select [
"" "no refs"
"a" "ref A"
"b" "ref B"
"ab" "ref A & B"
"c" "ref C"
"ac" "ref A & C"
"bc" "ref B & C"
"abc" "all refs"
] to-string replace/all reduce [
all [refa "a"]
all [refb "b"]
all [refc "c"]
] none ""
]
which certainly evaluates each refinement exactly once. Given that the
number of combinations in the "lookup table" is exponential with the
number of refinements, there's also clearly *some* point where it becomes
slower than the explicit decision tree.
Where that point is would clearly be a topic for a bit of benchmarking...
> I find it more rebolesque. It is easy to mantain. It is fast. It is not
> C-like. It can be also more simple, but less readable:
>
> print select [[none none none] "no refs" [true none none] "a"...] reduce [
> refa refb refc ]
>
... and likewise for this combinatorial version.
Thanks for the interesting alternatives!!!
-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" ;
[17/25] from: media:quazart at: 2-Nov-2001 7:37
Hey Romano,
I was just about to write the Exact same algorithm... you beat me to the
reply... I'll have to improve my lightning reflexes a bit... ;-)
Joel, the advantage of supplying each condition is that you can then trap
any COMBINATION by itself... not just if a value is set by itself... in
some algo rithms you must know if all, some or one of them is set and also
which one... you can re-code the any/all combo exactly like your last
if/then but then it does take a little more stack space in your brain to
see
it before coding it...
But once its on "paper" I find that any/all combos are much easier to
maintain, simply because the expressions themselves are easier to extract
visually... especially when there are many conditions and that the algorythm
is more than the height of your screeen also consider the following...
any [
all [refa refb refc print "condition a & b & c met"]
all [refa refb print "condition a & b met"]
all [refc]
print "nothing set"
]
now if you code that into if/else and the algorythm is quite long.... it
becomes quite a job to quickly see what is the quickest and best if/else
combo to build... there are many different possibilities and when you start
adding new conditions the algorithm just gets harder and harder to fix.
whereas the any and all combo still stays dead easy to maintain, cause you
just add one line.
But as you said, its just a question of fast to maintain vs fast to execute
vs fast to implement... I'm just trying to educate people into using
any/all which -sometimes- cuts my job a lot... just have to use it at the
right moment...
ciao!
-Max
----- Original Message -----
From: "Romano Paolo Tenca" <[rotenca--telvia--it]>
To: <[rebol-list--rebol--com]>
Sent: Thursday, November 01, 2001 7:20 PM
Subject: [REBOL] Re: Switch with refinements/hmmm!
> v: to-string replace/all reduce [ all[refa "a"] all [refb "b"] all [refc
c
]]
[18/25] from: joel:neely:fedex at: 2-Nov-2001 7:08
Hi, again,
Media wrote:
> Joel, the advantage of supplying each condition is that you can then trap
> any COMBINATION by itself... not just if a value is set by itself...
<<quoted lines omitted: 9>>
> But as you said, its just a question of fast to maintain vs fast to
> execute vs fast to implement...
That's *one* of the issues; the other is that the ALL trick shorts-out
if one of the subsequent expressions (subsequent to the things actually
being tested, that is) evaluates to FALSE or NONE. I firmly believe
that ALL is only appropriate if *every* expression's value should be
part of the continue/quit decision. When that is not the case, it's
easy to rewrite to signal which expressions are and are not part of that
decision, as follows:
any [
if all [refa refb refc] [print "A and B and C"]
if all [refa refb ] [print "A and B" ]
if all [refc ] [ ]
print "nothing set"
]
This variant has all the nice properties of compactness, visual separation,
maintainability, etc. of the ANY/ALL version, but also avoids confusion (or
induced bugs) over the issue of which expression values are tested.
As I mentioned previously, a discriminating scenario for this issue is
any [
if all [refa refb refc] [no-a: false print "A B C" ]
if all [refa refb ] [no-a: false print "A B" ]
if all [refc ] [no-a: true print "C" ]
no-a: true print "nothing"]
]
which would bail out prematurely (and erroneously) in the simple ANY/ALL
pattern.
-jn-
> ----- Original Message -----
> From: "Romano Paolo Tenca" <[rotenca--telvia--it]>
> To: <[rebol-list--rebol--com]>
> Sent: Thursday, November 01, 2001 7:20 PM
> Subject: [REBOL] Re: Switch with refinements/hmmm!
>
...
> >
> > print select [[none none none] "no refs" [true none none] "a"...] reduce [
> > refa refb refc ]
> >
The last version above is broken. It falls prey to the "literal words" issue
we've discussed several times regarding SELECT, since NONE and TRUE will
actually be 'NONE and 'TRUE in the above unless reduction is done. In addition,
the SELECT will fail unless the /ONLY refinement is used, since we're trying to
use a block as the "selection key". The simplest fix I've come up with so far
is
print select/only reduce [
reduce [none none none] "no refs"
reduce [true none none] "ref a"
...
] reduce [refa refb refc]
Incidentally, I'm working up a set of benchmarks on all of the variations
discussed in this thread; I'll publish results later today.
-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" ;
[19/25] from: rotenca:telvia:it at: 2-Nov-2001 14:40
> Hey Romano,
Hi, Media
> I was just about to write the Exact same algorithm...
Are we starting to Rebol-thinking?
I've seen that RT functions often use Select with code/function in block. It
remember me my first years in Assembly and auto-modifying code (i do not know
if this is the right expression in English).
In the HTTP part of Rebol, some functions are build token after token before
being called. For years I've spoken with programmers which sad that
reflexivity is bad, but i looked at the demo coder examples (and at the virus
and at the boot-strap of some OS) and I have always found an amazing and
superior logic in this kind of code.
> ciao!
---
Ciao, ciao
Romano
[20/25] from: media:quazart at: 2-Nov-2001 9:04
Hi Joel,
we are saying the same thing... as we both said, "any/all" improves
readability and then depending on the actual code you have to perform after
the "filter" you must just make sure that it works.
here is a little trick to improve speed ;-)
refa: true
refb: true
refc: false
any [
all [refa refb refc ( print "A and B and C" true)]
all [refa refb ( myvar: none print "A and B" true)]
all [refc ( print "nothing set" true)]
]
I'm not trying to prove anything or you wrong... I'm just giving you a
little trick to prevent a short cicuit. In most cases, by putting your
stuff in a little set of parenthesis and ending it with a true... you
prevent the expression from short-circuiting. Even though (like in this
example) I set something to none, the print statement still executes,
because all is only concerned with what the expression returns... in this
case its always true... so it wont evaluate anything else...
consider the following:
refa: true
refb: false
refc: true
any [
all [refa ( print "A " false)]
all [refb ( myvar: none print "B" false)]
all [refc ( print "C" false)]
]
here every true reference will be called...
peace ;-)
-MAx
[21/25] from: rotenca:telvia:it at: 2-Nov-2001 15:05
Hi, Joel
> use a block as the "selection key". The simplest fix I've come up with so
far
> is
> print select/only reduce [
<<quoted lines omitted: 4>>
> Incidentally, I'm working up a set of benchmarks on all of the variations
> discussed in this thread; I'll publish results later today.
I think, it is more fast to change only the value to select:
print select/only [
[none none none] "no refs"
[true none none] "ref a"
...
] load mold/only reduce [refa refb refc]
> -jn-
---
Ciao
Romano
[22/25] from: media:quazart at: 2-Nov-2001 9:19
Hi Romano
yep to all...
I use the switch function extensively. (it reminds me of a vector jump
table, I like that!)
ciao!
-MAxim
[23/25] from: greggirwin:mindspring at: 2-Nov-2001 13:01
Hi Romano,
<< auto-modifying code >>
Very close! "self-modifying code" is probably what we would say.
--Gregg
[24/25] from: joel:neely:fedex at: 2-Nov-2001 15:58
Hi, all, ...
Joel Neely wrote:
> Incidentally, I'm working up a set of benchmarks on all of the
> variations discussed in this thread; I'll publish results later
> today.
>
As promised, here are some comparisons. I took a bunch of
different variations as discussed in this thread, made a sub-
version of each one for 2, 3, and 4 refinements, and did some
timings. The text table below gives some key tradeoff points
and reports times. The test harness called each version of
each variation with all possible combinations of refinements,
and did so enough times to get vaguely stable net times.
Since I didn't have time to run the overall test multiple
times and average the results, all numbers should be viewed as
accurate +/- two seconds (or worse).
times vs.
refinements
------------
0? >1? code run mod flex 2 3 4
rs-either y n lin lin m hi m hi 12 26 54
rs-ifelse y n lin lin m hi m hi 13 27 55
rs-anyall1 y n lin lin m hi med 13 27 55
rs-if1 n n lin lin m hi m hi 17 38 80
rs-true n n lin lin v hi v hi 19 41 92
rs-pif n n lin lin v hi v hi 51 109 226
rs-ifelsetree y y exp lin m lo v hi 14 29 63
rs-eithertree y y exp lin m lo v hi 13 29 61
rs-alln n y lin lin v hi med 13 30 72
rs-anyalln y y exp exp m lo med 13 30 72
rs-anyparn y y exp exp med med 14 31 74
rs-anyifn y y exp exp med med 15 35 92
rs-ifn y y lin lin m hi v hi 22 49 109
rs-selwds y y exp exp m lo v lo 24 51 118
rs-combin y y lin lin med m lo 33 75 167
rs-selstr y y exp exp m lo v lo 35 79 178
rs-macro y y lin lin v hi m hi 35 80 180
rs-combo y y lin lin med v lo 66 152 337
rs-selblk y y exp exp m lo v lo 37 134 494
The "0?" column tells whether an approach explicitly handles the
case where no refinements are set.
The ">1?" column tells whether an approach handles more than one
refinement set per call.
The "code" column tells whether the source code for an approach
increases linearly or exponentially with the number of refinements.
The "run" column tells whether the execution time for an approach
increases linearly or exponentially with the number of refinements.
The "mod" column is my subjective impression of how easy the code
was to modify as the number of refinements increased.
The "flex" column is my subjective impression of how flexible the
code would be if different actions needed to be taken for the
various tested cases/combinations.
The timings are in seconds. Note however that increasing the number
of refinements doubles the number of combinations possible. The
benchmark put every routine through all possible combinations; the
following table shows timings normalized to remove that factor.
2 3 4
rs-either 3.00 3.25 3.38
rs-ifelse 3.25 3.38 3.44
rs-anyall1 3.25 3.38 3.44
rs-if1 4.25 4.75 5.00
rs-true 4.75 5.13 5.75
rs-pif 12.75 13.63 14.13
rs-eithertree 3.25 3.63 3.81
rs-ifelsetree 3.50 3.63 3.94
rs-alln 3.25 3.75 4.50
rs-anyalln 3.25 3.75 4.50
rs-anyparn 3.50 3.88 4.63
rs-anyifn 3.75 4.38 5.75
rs-ifn 5.50 6.13 6.81
rs-selwds 6.00 6.38 7.38
rs-combin 8.25 9.38 10.44
rs-selstr 8.75 9.88 11.13
rs-macro 8.75 10.00 11.25
rs-combo 16.50 19.00 21.06
rs-selblk 9.25 16.75 30.88
Descriptions/code for each of the tested alternatives appears below.
The functions were all written to append a string to a global variable
instead of printing (to avoid making the whole benchmark I/O bound).
Only the version with three refinements is shown for each algorithm.
RE-EITHER and RS-IFELSE are conventional if-elseif-elsif-... "ladders"
that take the first successful test, and have an else clause for no
matches. They only differ in the use of EITHER versus IF/ELSE.
rs-either-3: func [/refa /refb /refc][
either refa [
append rs-result "ref A"
][either refb [
append rs-result "ref B"
][either refc [
append rs-result "ref C"
][
append rs-result "NO ref"
]]]
]
rs-ifelse-3: func [/refa /refb /refc][
if/else refa [
append rs-result "ref A"
][if/else refb [
append rs-result "ref B"
][if/else refc [
append rs-result "ref C"
][
append rs-result "NO ref"
]]]
]
RS-ANYALL1 uses the ANY/ALL combination, bailing out after the first
true branch is found, with a default for no true option.
rs-anyall1-3: func [/refa /refb /refc] [
any[
all [refa append rs-result "ref A"]
all [refb append rs-result "ref B"]
all [refc append rs-result "ref C"]
append rs-result "NO ref"
]
]
RS-IF1 uses successive IF tests, exiting as soon as one is selected.
This version doesn't have a default case, but it would be easy to add.
rs-if1-3: func [/refa /refb /refc][
if refa [append rs-result "ref A" exit]
if refb [append rs-result "ref B" exit]
if refc [append rs-result "ref C" exit]
]
RS-TRUE uses the "inside-out-SWITCH" trick to select the first branch
where the associated word has a specified constant value.
rs-true-3: func[/refa /refb /refc][
switch true reduce [ ; needs a value
refa [append rs-result "ref A"]
refb [append rs-result "ref B"]
refc [append rs-result "ref C"]
]
]
RS-PIF uses Ladislav's polymorphic IF construction, with no default.
rs-pif-3: func[/refa /refb /refc][
pif [
refa [append rs-result "ref A"]
refb [append rs-result "ref B"]
refc [append rs-result "ref C"]
]
]
RS-EITHERTREE and RS-IFELSETREE use the most obvious structure -- a
fully-populated binary decision tree.
rs-eithertree-3: func [/refa /refb /refc] [
either refa
[either refb
[either refc
[append rs-result "all refs are set!"]
[append rs-result "ref A & B"]]
[either refc
[append rs-result "ref A & C"]
[append rs-result "ref A"]]]
[either refb
[either refc
[append rs-result "ref B & C"]
[append rs-result "ref B"]]
[either refc
[append rs-result "ref C"]
[append rs-result "NO refs"]]]
]
rs-ifelsetree-3: func [/refa /refb /refc] [
if/else refa
[if/else refb
[if/else refc
[append rs-result "all refs are set!"]
[append rs-result "ref A & B"]]
[if/else refc
[append rs-result "ref A & C"]
[append rs-result "ref A"]]]
[if/else refb
[if/else refc
[append rs-result "ref B & C"]
[append rs-result "ref B"]]
[if/else refc
[append rs-result "ref C"]
[append rs-result "NO refs"]]]
]
RS-ALLN uses a series of ALL clauses to test each refinement in
order, thus handling multiple true branches (but no default).
rs-alln-3: func [/refa /refb /refc][
all [refa append rs-result "ref A"]
all [refb append rs-result "ref B"]
all [refc append rs-result "ref C"]
]
RS-ANYALLN yses the full-blown ANY/ALL to test every possible
combination of refinement values. Note that it is order-sensitive.
rs-anyalln-3: func [/refa /refb /refc] [
any[
all [refa refb refc append rs-result "all refs are set !"]
all [refa refb append rs-result "ref A & B"]
all [refa refc append rs-result "ref A & C"]
all [refb refc append rs-result "ref B & C"]
all [refa append rs-result "ref A"]
all [refb append rs-result "ref B"]
all [refc append rs-result "ref C"]
append rs-result "NO ref"
]
]
RS-ANYPARN uses the "parens-in-an-all" trick to avoid short-circuit
exit of a selected branch.
rs-anyparn-3: func [/refa /refb /refc] [
any[
all [refa refb refc (append rs-result "all refs are set !")]
all [refa refb (append rs-result "ref A & B")]
all [refa refc (append rs-result "ref A & C")]
all [refb refc (append rs-result "ref B & C")]
all [refa (append rs-result "ref A")]
all [refb (append rs-result "ref B")]
all [refc (append rs-result "ref C")]
append rs-result "NO ref"
]
]
RS-ANYIFN uses an ANY to bail out after a branch has been chosen, but
uses IF for the individual branches to avoid the short-circuit exit
issue with ALL. This is a more generic version of RS-ANYPARN.
rs-anyifn-3: func [/refa /refb /refc] [
any[
if all [refa refb refc] [append rs-result "all refs are set !"]
if all [refa refb] [append rs-result "ref A & B"]
if all [refa refc] [append rs-result "ref A & C"]
if all [refb refc] [append rs-result "ref B & C"]
if all [refa] [append rs-result "ref A"]
if all [refb] [append rs-result "ref B"]
if all [refc] [append rs-result "ref C"]
append rs-result "NO ref"
]
]
RS-IFN uses a sequence of IF tests, wrapped in an overall test to
check for no true branches.
rs-ifn-3: func [/refa /refb /refc][
either any [refa refb refc] [
if refa [append rs-result "ref A"]
if refb [append rs-result "ref B"]
if refc [append rs-result "ref C"]
][
append rs-result "NO ref"
]
exit
]
RS-SELWDS creates a "key" block with the values of all refinements,
and uses that to SELECT/ONLY a value to be printed. The last non-
trivial line uses LOAD MOLD/ONLY REDUCE to get words instead of
NONE! or LOGIC! values in the key block.
rs-selwds-3: func [/refa /refb /refc] [
append rs-result select/only [
[none none none] "no refs"
[true none none] "ref A"
[none true none] "ref B"
[true true none] "ref A & B"
[none none true] "ref C"
[true none true] "ref A & C"
[none true true] "ref B & C"
[true true true] "all refs"
] load mold/only reduce [refa refb refc]
]
RS-COMBIN is a hand-optimized version of RS-COMBO. See that version
for a description.
rs-combin-3: func [/refa /refb /refc /local r n limit] [
r: make string! 6 n: 0 limit: 3
if refa [append r " A" n: n + 1]
if refb [append r " B" n: n + 1]
if refc [append r " C" n: n + 1]
append rs-result switch/default n reduce [
0 ["NO refs"]
limit ["all refs are set !"]
][join "ref" r]
]
RS-SELSTR creates a key value (as with RS-SELWDS) but the key is a
string instead of a block.
rs-selstr-3: func [/refa /refb /refc] [
append rs-result select [
"" "no refs"
"a" "ref A"
"b" "ref B"
"ab" "ref A & B"
"c" "ref C"
"ac" "ref A & C"
"bc" "ref B & C"
"abc" "all refs"
] to-string replace/all reduce [
all [refa "a"]
all [refb "b"]
all [refc "c"]
] none ""
]
RS-MACRO uses a special-purpose version of SWITCH to locate and
evaluate the appropriate block of expressions.
.rsmacro: func [block /default if-nothing /local todo] [
todo: clear []
foreach [word then-do] block [
if get word [append todo then-do]
]
if empty? todo [append todo if-nothing]
copy todo
]
rs-macro-3: func [/refa /refb /refc /local todo] [
todo: .rsmacro/default [
refa [append rs-result "is A"]
refb [append rs-result "is B"]
refc [append rs-result "is C"]
] [append rs-result "nothing to do"]
do todo
]
RS-COMBO uses a special-case function to test each refinement in
order, constructing a string with labels for all true cases. It
prints the result, but with special handling for all and none true.
rs-combo-3: func [/refa /refb /refc /local r n limit check] [
r: copy [] n: 0 limit: 3
check: func [opt label] [
if opt [repend r [pick ["&" ""] (n: n + 1) > 1 label]]
]
check refa "A"
check refb "B"
check refc "C"
append rs-result switch/default n reduce [
0 ["NO refs"]
limit ["all refs are set !"]
][join "ref " form r]
]
RS-SELBLK uses multi-layer REDUCEtion to ensure that the key block
and the selector blocks are all constructed with NONE! or LOGIC!
values.
rs-selblk-3: func [/refa /refb /refc] [
append rs-result select/only reduce [
reduce [none none none] "no refs"
reduce [true none none] "ref A"
reduce [none true none] "ref B"
reduce [true true none] "ref A & B"
reduce [none none true] "ref C"
reduce [true none true] "ref A & C"
reduce [none true true] "ref B & C"
reduce [true true true] "all refs"
] reduce [refa refb refc]
]
My thanks to all who contributed ideas. My apologies for not having
enough time to fully credit each idea to the appropriate contributors,
and for any errors I may have made in rendering your ideas.
-jn-
--
This sentence contradicts itself -- no actually it doesn't.
-- Doug Hofstadter
joel<dot>neely<at>fedex<dot>com
[25/25] from: media:quazart at: 5-Nov-2001 10:44
Thanks Joel,
This is a really nice act on your behalf.... man, doing this kind of
analysis takes time...
Its nice for the expectations to be confirmed or infirmed by the various
suggestions!
cool stuff!
I think this is a definite Rebol/Forces Submission material !!! what do you
think Allen?
-MAx
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted