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

[REBOL] Fun with blocks of words (a tad longish)

From: joel:neely:fedex at: 10-Oct-2001 7:39

Hello, all, I found the following little explorations instructive while thinking about words and contexts. I hope they are of interest/value to someone else. Let's make a block of words:
>> bow: []
== []
>> repeat i 5 [append bow i]
== [1 2 3 4 5] Woops! That gave me a block of integers, not a block of words. Besides which, I hate using variables unless necessary. I can get rid of the variable BOW (since REBOL is a very literal language) like this:
>> repeat i 5 [append [] i]
== [1 2 3 4 5] Now let's get a block of words instead of a block of integers:
>> repeat i 5 [append [] [i]]
== [i i i i i] But is that a block of five homographs (distinct words whose names are all spelled the same), or is it a block with five references to the same word?
>> reduce repeat i 5 [append [] [i]]
== [6 6 6 6 6] It's five references to the same word. Thought 1: REPEAT creates a context when executed. Within the block that is evaluated REPEATedly, the word that is the first argument to REPEAT is in that new context. (* Not considering the use of nested words that also create contexts, e.g. USE, etc...) But what if I *really* want a block of homographs? Hmmmm. Let's try sticking another context-creating word in there.
>> reduce repeat i 5 [use [.i] [.i: i append [] [.i]]]
== [5 5 5 5 5] Again we get five references to the same word! Thought 2: USE creates a context when executed. When word(s) in the first argument appear in the second argument, they are words in that context. It appears that there's a side-effect of "remembering" that fact so that subsequent USEs of the same block are still involved with that context. Hmmm. If I give USE a fresh second block every time, then I can avoid the side-effect.
>> reduce repeat i 5 [use [.i] copy [.i: i append [] [.i]]]
== [5 5 5 5 5] Duh! A simple COPY doesn't get to the .I that's in an additional layer of block nesting.
>> reduce repeat i 5 [use [.i] copy/deep [.i: i append [] [.i]]]
== [5] Double-duh! Now there's a fresh empty block for every evaluation of USE. Sigh... The only way I can think of to get around that is to re-introduce a word to serve as a "variable".
>> b: []
== []
>> reduce repeat i 5 [use [.i] copy/deep [.i: i append b [.i]]]
== [1 2 3 4 5] Aha! It looks like I (finally!) have five homographs! I'd really like to look at them, though...
>> repeat i 5 [use [.i] copy/deep [.i: i append b [.i]]]
== [.i .i .i .i .i .i .i .i .i .i] Rats! That's why I hate using "variables". Side-effects accumulate! While I'm at it, I'll keep the result this time so that I can look at the "raw" block and also REDUCE it:
>> b: []
== []
>> c: repeat i 5 [use [.i] copy/deep [.i: i append b [.i]]]
== [.i .i .i .i .i]
>> reduce c
== [1 2 3 4 5] Yes! Five homographs! Just what I wanted! Thought 3: COPY/DEEP is a way to avoid the side-effect that USE has on its second block. However, I have to remember that COPY/DEEP also blows away any side-effects on literal values! I wonder -- is there any way to get homographs without losing the ability to have literal values? How else can we create contexts? How about with a function?
>> apparg: func [b x] [append b [x]] >> c: repeat i 5 [apparg [] i]
== [x x x x x]
>> reduce c
== [5 5 5 5 5] Hmmmph! (Insert appropriate numbers of "duh" ;-) I'm back to getting five occurrences of the same word. Oh yeah... Thought 4: The context of a function is persistent. Subsequent uses of the function are using the same context. This isn't like ___ (insert name of commonly-used language here) which creates a new frame or environment for each invocation of a function. I suspect that this might be a performance choice. Create the context when the function is defined, and keep it around to avoid repeating that work every time the function is evaluated. Errrk? What about recursion?
>> rapparg: func [b i x] [
[ either i > x [b] [rapparg append b [i] i + 1 x] [ ]
>> c: rapparg [] 1 5
== [i i i i i]
>> reduce c
== [1 1 1 1 1] Well, it *looks like* I is getting saved and restored for the recursive calls, but let's make sure (by using some fairly gnarly-looking before-and-after tracing):
>> rapparg: func [b i x /local r] [
[ print b [ r: either i > x [b] [rapparg append b [i] i + 1 x] [ print r [ r [ ]
>> c: rapparg [] 1 5
2 3 3 4 4 4 5 5 5 5 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 == [i i i i i]
>> reduce c
== [1 1 1 1 1] Yup! The argument block is accumulating a bunch of references to the same word (an argument) whose value is being saved and restored to make the recursive call behave like one would expect it to. Thought 5: I suspect that means that there's a time penalty for using recursion (compared to repeatedly calling the same function within an iteration) but that benchmark will have to wait for another day. Wait a minute! My attention is wandering! I wanted to make a block of homographs. How else can I make new contexts? Whoa! I can make a new function everytime I iterate!
>> c: repeat i 5 [do func [b x] [append b [x]] [] i]
== [x x x x x]
>> reduce c
== [1 2 3 4 5] Yeehah! Now I get a fresh context (and therefore a fresh word) every time. Thought 6: FUNC appears *not* to have the side-effect of altering its second block (in the way that USE does). Subsequent uses of FUNC ... ... really get me a new context for each evaluation. Of course, being stubborn as well as simple-minded, I can't help but think about the idea of USE inside the body of a function. Since USE seems to have a side-effect on its second (literal) argument, I would expect to get the same word over and over. Let's try it...
>> appuse: func [b x] [use [.x] [.x: x append b [.x]]] >> c: repeat i 5 [appuse [] i]
== [.x .x .x .x .x]
>> reduce c
== [1 2 3 4 5] Wot??? I got five homographs! Does USE inside a function body behave differently than USE at the console level?
>> repfun: func [b x] [repeat i x [use [.x] [.x: i append b [.x]]]] >> c: repfun [] 5
== [.x .x .x .x .x]
>> reduce c
== [1 2 3 4 5] It does! We're back to five homographs, even though the body of REPFUN is essentially the same as the unsucessful attempt from the console prompt earlier (except for using an argument instead of a literal as the object of the APPEND). Thought 7: There's more going on here than meets the eye. I need another cup of coffee. As usual, feedback/comments/corrections/coffee are welcome! ;-) -jn- -- joelDOTneelyATfedexDOTcom