Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

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