to-char
[1/25] from: izkata:aol at: 9-Feb-2002 17:59
How do you use the to-char function?
I've tried everything I can think of.
Daniel S.
[2/25] from: brett:codeconscious at: 11-Feb-2002 9:56
Not sure what you want to do so here are a couple of possibilities:
to-char "*"
to-char 42
going the other way:
to-integer #"^-"
Brett.
[3/25] from: izkata:aol at: 10-Feb-2002 21:05
sorry, I'm not used to the harder stuff like
parsing, yadda, yadda, yadda.
What I'm looking for is a function/native that
will change a word into a block of characters,
for example:
tochar "Hello"
Word: [
H
e
l
l
o
]
Daniel S
[4/25] from: brett::codeconscious::com at: 11-Feb-2002 13:30
Read up on series in the manual. A string is a series. A block is a series.
For example,
first "Hello"
pick "Hello" 3
repeat a-char "Hello" [print a-char]
If you need the characters seperately in a block:
block-of-char: copy []
repeat a-char "Hello" [
append block-of-char a-char
]
Now perhaps you can help me. What does "yadda" actually mean?
Brett.
[5/25] from: philb:upnaway at: 11-Feb-2002 11:27
Hi Daniel,
try something like
cb: copy []
foreach lv-char "abcd" [append cb lv-char]
probe cb
just need to wrap it in a function.
Cheers Phil
=== Original Message ===
sorry, I'm not used to the harder stuff like
parsing, yadda, yadda, yadda.
What I'm looking for is a function/native that
will change a word into a block of characters,
for example:
tochar "Hello"
Word: [
H
e
l
l
o
]
Daniel S
[6/25] from: vonj:peoplepc at: 10-Feb-2002 21:24
It's indicative of blah, blah, blah
[7/25] from: carl:cybercraft at: 11-Feb-2002 17:47
On 11-Feb-02, [Izkata--aol--com] wrote:
> sorry, I'm not used to the harder stuff like
> parsing, yadda, yadda, yadda.
<<quoted lines omitted: 9>>
> o
> ]
Hi Daniel,
I notice Brett has just posted similar stuff to the following, but
this is what I came up with anyway...
This function should do what you want...
to-chars: func [str /local word][
word: copy []
forall str [append word str/1]
word
]
>> to-chars "hello"
== [#"h" #"e" #"l" #"l" #"o"]
>> to-chars "goodbye"
== [#"g" #"o" #"o" #"d" #"b" #"y" #"e"]
However, depending on what you're doing, you may be able to get the
results you want without needing to convert a string to a block, as a
string is already a series of characters. So, the following gives
the same results...
>> str: "hello"
== "hello"
>> str/2
== #"e"
>> word: to-chars str
== [#"h" #"e" #"l" #"l" #"o"]
>> word/2
== #"e"
HTH.
--
Carl Read
[8/25] from: izkata:aol at: 11-Feb-2002 11:42
I've looked at all the responses so far, and
it's almost what I wanted. Heres a better example:
to-charblock: "Hi"
would make:
block: [
H
i
]
then,
H: [print "*** **** ****"]
i: [print "*** iiii"]
so,
do block
*** **** ****
*** iiii
I am going to use a combination of
* and spaces to make a mouth that would
'talk' to you.
I am going to try what was suggested, though.
Daniel S.
[9/25] from: jason:cunliffe:verizon at: 10-Feb-2002 23:37
; Is this what you want?
to-charblock: func[aword [string!] /local charblock letter][
charblock: []
clear charblock
foreach letter aword [
append charblock letter
]
return charblock
]
h: to-charblock "hello"
; to access >> print h/1 etc..
hth
./Jason
[10/25] from: joel:neely:fedex at: 11-Feb-2002 20:40
Hi, Jason,
Small kibitz...
Jason Cunliffe wrote:
> ; Is this what you want?
> to-charblock: func[aword [string!] /local charblock letter][
<<quoted lines omitted: 5>>
> return charblock
> ]
Would either
charblock: copy []
or
charblock: clear []
work as well for you?
-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" ;
[11/25] from: jason:cunliffe:verizon at: 12-Feb-2002 0:20
> Would either
>
> charblock: copy []
>
> or
>
> charblock: clear []
>
> work as well for you?
Hi Joel
Yes - just hasty cutnpaste from console while trying out some other stuff.
I am curious what preferences/differences are between:
copy [] and clear []
thanks
./Jason
[12/25] from: carl:cybercraft at: 12-Feb-2002 19:24
Hi again Daniel,
On 12-Feb-02, [Izkata--aol--com] wrote:
> I've looked at all the responses so far, and
> it's almost what I wanted. Heres a better example:
<<quoted lines omitted: 4>>
> i
> ]
Ah - you actually want 'H and 'i there to be REBOL "word"s, not REBOL
char
acters - except they're just 1-letter words.
> then,
> H: [print "*** **** ****"]
<<quoted lines omitted: 3>>
> *** **** ****
> *** iiii
As you have it there, the 'do wouldn't work as you expect, as this
sesion at the REBOL Console shows...
>> block: [H i]
== [H i]
>> H: [print "*** **** ****"]
== [print "*** **** ****"]
>> i: [print "*** iiii"]
== [print "*** iiii"]
>> do block
== [print "*** iiii"]
Which isn't the result you want. However, if 'H and 'i are made to do
their block (pun not intended:) using 'does, you get what you want...
>> H: does [print "*** **** ****"]
>> i: does [print "*** iiii"]
>> do block
*** **** ****
*** iiii
(Testing stuff a bit at a time at the Console is a good way to work
things out.)
'does turns 'H and 'i into functions (without any arguments), the
difference being you'll get the result returned by the function,
whereas when they were just blocks, the block is what you get. Um,
this probably explains it better...
>> a-block: [print "something"]
== [print "something"]
>> a-block
== [print "something"]
>> a-block: does [print "something"]
>> a-block
something
Finally, to get words instead of characters into the block, you can
use 'to-word. Except it can't convert a character directly to a
word, (ie, an #"H". That's a character, whereas "H" is a string.),
so you need to convert each character in your string into a
1-character string before you can convert it into a word. I know it
sounds convoluted, but the function to do what you want is a lot
shorter than this paragraph you'll be happy to hear. (: ...
to-charblock: func [str [string!] /local blk][
blk: copy []
foreach letter str [append blk to-word to-string letter]
blk
]
You should be able to cut and paste that function directly into the
Console to test it, and assuming you can, it can be used to give
these results (as long as you've also entered the 'H and 'i functions
first in the way I suggested above)...
>> block: to-charblock "Hi"
== [H i]
>> do block
*** **** ****
*** iiii
Have we got it right now? Though don't be afraid to say no if we
haven't - it's what the list is for.
> I am going to use a combination of
> * and spaces to make a mouth that would
> 'talk' to you.
> I am going to try what was suggested, though.
> Daniel S.
--
Carl Read
[13/25] from: carl:cybercraft at: 12-Feb-2002 18:14
Hi Jason,
Just a note to point out you didn't need to make 'letter local in your
function as it's automatically made local to 'foreach's block. ie...
>> foreach letter "abc" [print letter]
a
b
c
>> print letter
** Script Error: letter has no value
** Near: print letter
Same with 'for and so on. Even so, for sanity's sake I don't recomend
you do much of the following kind of stuff...
>> for n 1 3 1 [prin [n ": "] for n 1 3 1 [prin n] print ""]
1 : 123
2 : 123
3 : 123
(:
On 11-Feb-02, Jason Cunliffe wrote:
> ; Is this what you want?
> to-charblock: func[aword [string!] /local charblock letter][
<<quoted lines omitted: 9>>
> hth
> ./Jason
--
Carl Read
[14/25] from: joel:neely:fedex at: 12-Feb-2002 7:12
Hi, Jason,
Forgive me if I over-explain. I don't mean to insult your
intelligence/experience. This kind of question comes up
periodically, so I thought I'd cover all the basics in case
any newcomers to the list are puzzled by this (or related)
issues.
Jason Cunliffe wrote:
> I am curious what preferences/differences are between:
>
> copy [] and clear []
>
(All of my remarks below actually apply to series values in
general, not just blocks. However, the term "block" is nicer
to write about, because its singular and plural forms are easy
to distinguish and pronounce, unlike the term "series". For
that -- fairly feeble -- reason I'll stick with "block".)
The differences have to do with "current size" versus "capacity"
and the persistence of literal block values.
If you type either of the above at the console, you get an empty
block, and that's just about the end of the story. Embedding
either expression inside a block that is DOne or inside a function
gets quite a different effect.
First, let's talk about "current size" and "capacity". A block
is like an array; it contains a specific set of values in a
specific order. But REBOL blocks are flexible; we can extend
a block with more values or remove values from a block.
When first created, a block has some (optional) content, but also
can have additional "growing room" already allocated but not yet
in use. When we insert a value into a block, we use up one "slot"
of that additional space, assuming that there is some still left.
Once there's no more growing room, the interpreter must allocate
enough memory for the new (expanded) state of the block, and then
copy all of the old and new data into the new space.
Therefore, when creating or expanding a block, there's a tradeoff
between only providing enough room for the current content (thus
increasing the probability that some copying will need to be done
later) versus providing lots of growing room (thus increasing the
probability that memory is tied up "just in case" but never used,
and possibly interfering with other uses).
I don't know what the exact (current) policies and defaults are;
perhaps someone else (RT?) can shed more light on that mystery.
I *do* know that a literal block
[]
appearing in a REBOL expression like
fleegle: []
gets translated into a block value with no (initial) content and
only limited growing room (maybe none? anybody know?).
If we know that we are going to be growing a block, we can ask
in advance for a certain capacity, even if we're not immediately
populating it with data, by writing something like
fleegle: make block! 1000
to indicate how much space we think we'll need. To see what a
difference that can make, consider the following expressions
and the time required to evaluate them (I simply wrapped each
one as follows:
t: now/time/precise ... now/time/precise - t
to get the times.)
foo: []
repeat i 10000 [insert tail foo i] ==> 0:00:00.77
foo: []
repeat i 100000 [insert tail foo i] ==> 0:00:58.01
Increasing the number of iterations by a factor of ten takes
*much* more than ten times as long, because the overhead of
allocating new space and copying values takes progressively
more of the total time. (I'd expect it to be quadratic;
ignoring the jitter due to garbage collection, that appears
to be the case. Again, perhaps someone can confirm or deny.)
foo: make block! 100000
repeat i 100000 [insert tail foo i] ==> 0:00:02.31
If we pre-allocate the amount of storage we'll need, then
the interpreter doesn't have to waste time shuffling things
around to make more room. A *huge* improvement!
foo: clear foo
repeat i 100000 [insert tail foo i] ==> 0:00:01.98
This case shows that CLEAR removes the content, but leaves
the capacity unchanged. Therefore we can re-use the space
with no time penalty.
baz: clear foo
repeat i 100000 [insert tail baz i] ==> 0:00:01.97
Likewise, we can get at that space through multiple references
with the same benefit.
Now let's talk about persistence. The simplest example I can
think of is this one:
>> to-do: [foo: [] insert tail foo "Hi!" print foo]
== [foo: [] insert tail foo "Hi!" print foo]
>> do to-do
Hi!
No surprise thus far, but look at what happens next...
>> to-do
== [foo: ["Hi!"] insert tail foo "Hi!" print foo]
>> do to-do
Hi! Hi!
>> foo
== ["Hi!" "Hi!"]
>> to-do
== [foo: ["Hi!" "Hi!"] insert tail foo "Hi!" print foo]
The key is to recognize that TO-DO (initially) *isn't* saying
set FOO to refer to an empty block
but rather is saying
set FOO to refer to *this* block
-- the second element in
TO-DO is the *this* part. Subsequent changes via the reference
in FOO affect the underlying data series, regardless of how
many other references we may have to it -- including the one
that's the second element of TO-DO! Therefore, the second
element of TO-DO is being continually changed.
The same thing can happen with a literal block appears in the
(source for the) body of a function. If we don't want that
block to keep accumulating the effects of expressions that
the interpreter evaluates subsequently, we need to make sure
that our variable is set to an empty block each time. There
are three obvious ways to do this (and several un-obvious
ways as well! ;-)
foo: copy []
asks for a clone of its argument block; it doesn't alter that
argument block. Unless you do some fairly contorted things
(which can be done!), evaluating that will produce an empty
block every time.
foo: make block! 1
(using whatever size you wish) explicitly asks the interpreter
to construct a fresh block. This just looks clunkier for small
cases, but is nicely explicit when you have a definite idea
of how much space you'll need. If you *don't* know how much
you'll need, and allocate too much "just in case", this version
wastes memory.
foo: clear []
explicitly asks the interpreter to empty out anything that might
have accumulated from previous evaluations. It throws out the
content, but doesn't change the allocated space. If you are
repeatedly evaluating a function that constructs results of
roughly similar size (e.g., building up strings used as output
lines in a printable report), then this version allows the
capacity of the underlying series to grow upwards as it is
actually used. It avoids the overhead of COPYing or MAKEing a
new block for each evaluation of the function, which is A Good
Thing. However, if you have a single extreme case that builds
up a huge value, this version will keep all of that space
allocated, even if subsequent evaluations don't need it. The
risk of tying up memory for no good reason is A Bad Thing, but
you'll have to figure out on a case-by-case basis how big that
risk actually is.
Speaking of risk, I hope I haven't given you a two-ton answer
to a five-ounce question! ;-)
HTH!
-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" ;
[15/25] from: g:santilli:tiscalinet:it at: 12-Feb-2002 14:21
At 03.40 12/02/02, Joel wrote:
>> to-charblock: func[aword [string!] /local charblock letter][
>> charblock: []
<<quoted lines omitted: 10>>
> charblock: clear []
>work as well for you?
Oh and BTW, in the second case (and the one used above) you will
have problems if you write something like:
a: to-charblock "123"
b: to-charblock "456"
(Just in case Jason doesn't know...)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[16/25] from: joel:neely:fedex at: 12-Feb-2002 8:52
Hi, Gabriele,
Gabriele Santilli wrote:
> >or
> >
<<quoted lines omitted: 5>>
> a: to-charblock "123"
> b: to-charblock "456"
In both cases, I got the following:
>> a: to-charblock "123"
== [#"1" #"2" #"3"]
>> b: to-charblock "456"
== [#"4" #"5" #"6"]
What kinds of problems are you referring to?
-jn-
[17/25] from: rotenca:telvia:it at: 12-Feb-2002 18:16
Hi Joel,
> > Oh and BTW, in the second case (and the one used above) you will
> > have problems if you write something like:
<<quoted lines omitted: 7>>
> >> b: to-charblock "456"
> == [#"4" #"5" #"6"]
I think that Gabriele want to say this:
>> x: func[x] [head insert clear [] x]
>> a: x 1
== [1]
>> b: x 2
== [2]
>> a
== [2]
---
Ciao
Romano
[18/25] from: carl:cybercraft at: 13-Feb-2002 9:51
On 13-Feb-02, Joel Neely wrote:
> Hi, Gabriele,
> Gabriele Santilli wrote:
<<quoted lines omitted: 17>>
> == [#"4" #"5" #"6"]
> What kinds of problems are you referring to?
Romano was right (in his following mail) - 'a and 'b would be the same
after doing the above.
When I noticed there wasn't a 'copy with the block in to-charblock I
checked it to see what would happen, expecting the blocks returned to
just keep getting longer and longer. When they didn't, and seemed to
be doing what was expected, I checked the function again and noticed
the 'clear this time. I wondered about its use then, (instead of
'copy), but didn't look into it.
The short answer to this (for newcomers) is it's better to use...
a-block: copy []
within functions instead of...
a-block: []
clear a-block
Examples...
>> copy-block: func [a][b: copy [] append b a]
>> a: copy-block 1
== [1]
>> b: copy-block 2
== [2]
>> a
== [1]
>> b
== [2]
>> clear-block: func [a][b: [] clear b append b a]
>> c: clear-block 3
== [3]
>> d: clear-block 4
== [4]
>> c
== [4]
>> d
== [4]
And finally, what happens with no 'copy or 'clear...
>> append-block: func [a][b: [] append b a]
>> e: append-block 5
== [5]
>> f: append-block 6
== [5 6]
>> e
== [5 6]
>> f
== [5 6]
--
Carl Read
[19/25] from: atruter:hih:au at: 13-Feb-2002 12:06
Hi Joel,
> Forgive me if I over-explain. I don't mean to insult your
> intelligence/experience. This kind of question comes up
> periodically, so I thought I'd cover all the basics in case
> any newcomers to the list are puzzled by this (or related)
> issues.
I find your explanations much better than the (sometimes terse) RT doco. It
certainly "brings things together" for me, keep up the good work!
Regards,
Ashley
P.S. I think you have generated enough material for your own book on REBOL,
I for one would buy if you ever publish ;)
[20/25] from: jason:cunliffe:verizon at: 13-Feb-2002 15:31
From: "Joel Neely" <[joel--neely--fedex--com]>
> Forgive me if I over-explain. I don't mean to insult your
> intelligence/experience. This kind of question comes up
> periodically, so I thought I'd cover all the basics in case
> any newcomers to the list are puzzled by this (or related)
> issues.
Joel
Thanks very much. Your explanation and the posts which followed were just
what I was hoping for.
So now let me ask an even more basic [FA] question:
What is happening, what is the real meaning of
someblock: []
What is going on when I call it within a function and then call the function
repeatedly [when the previous values remain].
ie: why do I _really_ need clear or copy?
Would like to see more helpful doc lookups for : and []
>> ? :
** Syntax Error: Invalid word-get -- :
** Near: (line 1) ? :
>> ? []
[] is a block
>> ? clear
USAGE:
CLEAR series
DESCRIPTION:
Removes all values from the current index to the tail. Returns at tail.
CLEAR is an action value.
ARGUMENTS:
series -- (Type: series port none)
thanks
./Jason
[21/25] from: carl:cybercraft at: 14-Feb-2002 12:46
On 14-Feb-02, Jason Cunliffe wrote:
> From: "Joel Neely" <[joel--neely--fedex--com]>
>> Forgive me if I over-explain. I don't mean to insult your
<<quoted lines omitted: 5>>
> Thanks very much. Your explanation and the posts which followed were
> just what I was hoping for.
I'll give it a go, though the terms I use may not all be comp-science
perfect...
> So now let me ask an even more basic [FA] question:
A question for you - what's "FA"? (:
> What is happening, what is the real meaning of
> someblock: []
An empty block is created, as is the word 'someblock, which points to
(references) the block. If you now made another word to reference
someblock, they would both be referencing the same block. A Console
example...
>> someblock: []
== []
>> not-another-block: someblock
== []
>> append someblock "abc"
== ["abc"]
>> someblock
== ["abc"]
>> not-another-block
== ["abc"]
> What is going on when I call it within a function and then call the
> function repeatedly [when the previous values remain]. ie: why do I
> _really_ need clear or copy?
The reason can be seen in the above, in that it remains the same
block. Meaning you haven't made a copy of the block. Continuing on
with the above, this shows the difference 'copy makes...
>> another-block: copy someblock
== ["abc"]
>> append someblock "def"
== ["abc" "def"]
>> someblock
== ["abc" "def"]
>> another-block
== ["abc"]
> Would like to see more helpful doc lookups for : and []
The Core Guide on RT's site is very good here. (It's a full copy of
the book version.) See the "series" and "block" sections, blocks
being a subset of series.
>>> ? :
> ** Syntax Error: Invalid word-get -- :
> ** Near: (line 1) ? :
The ":" is just used to define a word - it's says "make this word
reference what follows". In the case of series such as blocks and
strings, the word is used as an index to the series. So words can be
used thus...
>> append someblock ["ghi" "jkl"]
== ["abc" "def" "ghi" "jkl"]
That adds two more strings to 'someblock...
>> someblock
== ["abc" "def" "ghi" "jkl"]
'next in the following looks at the block from the next value onwards
from where 'someblock's index is pointing. (Which is the first value
in the block.)
>> next someblock
== ["def" "ghi" "jkl"]
And this creates the word 'a-word to reference the block at that
second position...
>> a-word: next someblock
== ["def" "ghi" "jkl"]
>> a-word
== ["def" "ghi" "jkl"]
And now to see what the words' indexes are...
>> index? someblock
== 1
>> index? a-word
== 2
And finally, proof (using the 'head word) that 'a-word can reference
the full block...
>> head a-word
== ["abc" "def" "ghi" "jkl"]
>>> ? []
> [] is a block
Blocks are very inportant in REBOL, but they're quite a simple
concept, being just a container for other stuff.
>>> ? clear
> USAGE:
<<quoted lines omitted: 5>>
> ARGUMENTS:
> series -- (Type: series port none)
Clear just empties a series of its values, (from the index-point
onwards of the word you're using to reference it), and so the series
still exists after a 'clear, its just empty, like "" is an empty
string, or [] is an empty block.
More on functions: A series is created when the function is first
created and not each time the function is called, which is why what's
in a series will persist from function-call to function-call unless
you specifically clear it or make a copy of it. Whether to use 'copy
or 'clear (or neither for that matter) will depend on the behaviour
you want from the series.
HTH, and that I've got it all more or less right. Others here will (I
hope) correct me where I'm wrong.
--
Carl Read
[22/25] from: jason:cunliffe:verizon at: 13-Feb-2002 20:53
> > So now let me ask an even more basic [FA] question:
>
> A question for you - what's "FA"? (:
FA: "frequently asked" ; as in FAQ => why need to use copy []
./Jason
[23/25] from: izkata:aol at: 14-Feb-2002 8:56
Yes, this is it perfectly!
I hadn't known about putting
'does in right there, which is
why my other tests hadn't worked
out.
Thanks for the help!
P.S, Why does it seem that the
only time mail is ever sent to the
mailing list is in the middle of the
night (for me)?
(Central time zone)
[24/25] from: joel:neely:fedex at: 14-Feb-2002 9:08
Hi, Jason, Carl, and all...
Sorry that the following is so long. I don't have time to
write it in fewer words. English, combined with a bag of
prior experience with other programming languages, is really
a *TERRIBLE* medium for explaining REBOL!!! ;-)
As always, I'll be happy for any corrections or useful
revisions of this discussion.
Carl Read wrote:
> > So now let me ask an even more basic [FA] question:
>
> A question for you - what's "FA"? (:
>
FA as in FAQ
> > What is happening, what is the real meaning of
>
> > someblock: []
>
> An empty block is created, as is the word 'someblock, which
> points to (references) the block.
>
I must respectfully disagree. The issue of when values are
created is an entirely different discussion than the issue
of what happens when those values are evaluated. There is
not much documentation that separates out these issues, so
this puzzle comes up in almost every REBOL programmer's
path to enREBOLment.
As I understand it, when REBOL *loads* a string of the form
foo: <value>
(where <value> is a single value such as a number, string, or
block) it creates an internal REBOL structure (which can serve
as both code and data, depending on how it is subsequently
used). In that structure there is a distinct REBOL value for
each syntactical element in the source string. However, to
know some of the details, we have to know where that string
came from.
Console input to the interpreter is submitted to a "load-and-do"
cycle which takes the input string, loads (translates) it into
a REBOL structure, then DOes that structure. Starting with a
fresh REBOL process, we can model that process as follows:
REBOL/View 1.2.1.3.1 21-Jun-2001
... more verbiage suppressed ...
*** Obtain REBOL/View/Pro from http://www.rebol.com/view-sales.html
>> print foo
** Script Error: foo has no value
** Near: print foo
(Just to show that REBOL has no preconceived notion of FOO...)
>> console-input-1: "foo: []"
== "foo: []"
>> length? console-input-1
== 7
The console input is a string of 7 characters.
>> console-struct-1: load console-input-1
== [foo: []
]
>> length? console-struct-1
== 2
LOADing the console input produces a block containing two values.
>> type? console-struct-1/1
== set-word!
>> type? console-struct-1/2
== block!
LOAD created a SET-WORD! value from the string "foo:" and created a
block from the string "[]" and put those two values into a new block.
The (empty at this point) block doesn't have any more baggage, but
there's a hidden issue with the SET-WORD! value.
Each REBOL word belongs to a context (known in other languages as an
environment
or other terms even less useful to us right now! ;-)
I think of an environment as a dictionary that pairs the name of a
word with a value (reference for some types), but that pairing is
relevant only within that context. (If that last phrase is unclear,
hang on; we'll try to shed more light on it Real Soon Now.) I think
of the internal representation of a word as "containing" a reference
to the string that is its name and a reference to its context.
NB: THAT IS A DESCRIPTIVE MENTAL MODEL. THERE ARE MANY WAYS THAT
THIS COULD ACTUALLY BE IMPLEMENTED; I DON'T KNOW WHICH OF THEM
IS/ARE ACTUALLY USED IN THE VARIOUS FLAVORS OF REBOL.
In order to create the SET-WORD! value for "foo:", it must have a
context. For the above case, since there's nothing to specify
otherwise, that will be the global context. So a word value is
created with a name of "foo" and a reference to the global context;
a new definition is added to the global context containing a word
name of "foo" but with no associated value at this point.
(AGAIN, THINK OF THIS AS METAPHORICAL.)
Finally (whew! this guy is long-winded! ;-) we're ready to talk
about the DO step.
>> do console-struct-1
== []
>> print foo
>> print mold foo
[]
DOing a block requires evaluating each value within the block.
When evaluating a SET-WORD! the interpreter does something like
the following (AGAIN, METAPHORICAL):
1) put the word in question "on hold";
2) evaluate the following value/expression (let's not go into
that too much right now) which in this case is a reference
to a block;
3) evaluating a block (NOT the same as DOing the block!) simply
yields a reference to that block;
4) take the word left on hold in (1) and find its context;
5) within that specific context/dictionary, alter the value slot
associated with that word, so that now the value slot contains
the value (or reference to ... yadda yadda) produced in (3);
6) in addition, the value from (3) now serves as the value for
this entire process (in case this evaluation occurred within
a larger evaluation -- the issue we skipped over in (2)).
At this point, the interpreter would be able throw away a string
typed into the console, and the block created from that string,
since there are no surviving references to them.
In the case of our little modeling exercise, that doesn't happen
because we actually have words that are set to the string and
block we're playing with. We'll keep them around for a little
longer to make a point.
Let's model the load-and-do cycle on another string (this time
without so much verbiage):
>> console-input-2: "oof: foo"
== "oof: foo"
>> console-struct-2: load console-input-2
== [oof: foo
]
>> do console-struct-2
== []
There's a new word in the global context now. Its value (in that
context) is set to refer to the *same* block that (global) FOO is
set to. Let's keep modeling the load-and-do cycle...
>> console-input-3: "append oof 1"
== "append oof 1"
>> console-struct-3: load console-input-3
== [append oof 1
]
>> do console-struct-3
== [1]
I'm sure we can all describe that one, and would anticipate the
result of cheating and looking at the actual words we're playing
with in our model:
>> mold foo
== "[1]"
>> mold oof
== "[1]"
However, ("Finally!" you're probably thinking ;-) now I can get
to my first punch line. Let's go back and look at our input
strings, and then look at the structures that represent those
strings in REBOL internal form (with MOLDing and some added
whitespace for clarity):
>> foreach thing reduce [
[ console-input-1 console-input-2 console-input-3
[ ][print mold thing]
"foo: []"
"oof: foo"
"append oof 1"
>> foreach thing reduce [
[ console-struct-1 console-struct-2 console-struct-3
[ ][print mold thing]
[foo: [1]
]
[oof: foo
]
[append oof 1
]
What's with the value of CONSOLE-STRUCT-1??? Remember that it
originally contained two values -- a set-word! and an empty
block (created empty at the time that CONSOLE-INPUT-1 was
LOADed).
And that's the key.
DOing CONSOLE-STRUCT-1 didn't *create* the set-word nor the
empty block. They were created when CONSOLE-INPUT-1 was LOADed.
All that happened when CONSOLE-STRUCT-1 was DOne was that the
value (in the global context) for FOO was set to (a reference
to) the block which *already* existed and was referred to in
the second position of CONSOLE-STRUCT-1.
DOing CONSOLE-STRUCT-2 set (global) OOF (created when we LOADed
CONSOLE-INPUT-2) to refer to that same block (still empty at
that time).
At that point there were three references to that block: the
original reference in CONSOLE-STRUCT-1, in the global context
for FOO, and in the global context for OOF.
The first of those only remained in existence because of our
modeling; if we simply typed the console input strings in at
the prompt, the older strings and blocks would have already
gone back to the recycling plant as soon as the next input
was typed.
Just to prove that these are three references to the same
block, let's cheat on our model. We'll set OOF directly and
see the consequences.
>> oof: "no block here!"
== "no block here!"
>> foreach thing reduce [
[ console-struct-1 console-struct-2 console-struct-3
[ ][print mold thing]
[foo: [1]
]
[oof: foo
]
[append oof 1
]
Setting OOF to a new string (created when this new console
input was loaded -- outside our model), we simply change the
global dictionary definition for OOF to something else. We
haven't altered the value with which OOF was associated
before that point.
>> oof: append foo "I'm back!"
== [1 "I'm back!"]
Now we've reSET the value of OOF to the value of an expression
that *also* mutates the value to which FOO is set. Therefore,
we now see the effect of that mutation through all references
to that same value:
>> foreach thing reduce [
[ console-struct-1 console-struct-2 console-struct-3
[ ][print mold thing]
[foo: [1 "I'm back!"]
]
[oof: foo
]
[append oof 1
]
With all of that in place, let's fast-forward to Carl's comments
on functions:
> More on functions: A series is created when the function is
> first created and not each time the function is called, which is
> why what's in a series will persist from function-call to
> function-call unless you specifically clear it or make a copy of
> it. Whether to use 'copy or 'clear (or neither for that matter)
> will depend on the behaviour you want from the series.
>
I agree 100% with what Carl meant, but -- with apologies -- let me
try to reword a little bit by continuing our modeling exercise.
>> console-input-4: "trick: func [/local foo] [foo: append [] 1]"
== "trick: func [/local foo] [foo: append [] 1]"
>> console-struct-4: load console-input-4
== [trick: func [/local foo] [foo: append [] 1]
]
>> do console-struct-4
Now there's a global word TRICK which is set to a FUNCTION! value.
The SECOND part of a FUNCTION! value is a block -- the "body" of
the function.
>> second :trick
== [foo: append [] 1]
When was that FUNCTION! value created? When CONSOLE-STRUCT-4 was
DOne. When FUNC is applied to two blocks, it constructs a new
FUNCTION! value with a process something like this:
1) create a new (empty) context;
2) add to that context every argument and refinement in the
first block given to FUNC;
3) make a deep copy of the second block offered to FUNC, but
whenever a word appears in that copy that is also in the
first block, change the context of the word IN THE COPY to
be the context created in (2);
4) create a new FUNCTION! value that is based on the results
of (2) and (3), and return that FUNCTION! as the value of
(this invocation of) FUNC to whatever caused FUNC to be
invoked.
The third element in the body of TRICK is (at this moment!) an
empty block. It is there because LOAD created an empty block as
the third element of the fourth element of CONSOLE-STRUCT-4, and
then FUNC copied that empty block at (3) to create the third
element of the block that serves as the body of the FUNCTION!
created in (4).
So, at this moment, the third element of the body of TRICK is
an empty block created by copying an empty block created by
LOADing a string that contained -- in part -- a #"[" followed
by a #"]". To save me some typing and you some reading, let's
call that block ~HERBIE~ (the weird punctuation is to remind
us that this is only our conversational name for something,
it is not REBOL terminology nor notation).
Now when we evaluate the (global) TRICK, we get the same behavior
that we were discussing earlier:
>> trick
== [1]
>> trick
== [1 1]
>> trick
== [1 1 1]
The reason is that -- in the body of TRICK -- the (local to
TRICK) word FOO is set to the value of an expression that
mutates its first argument, which is ~HERBIE~. ~HERBIE~ started
off empty when the function was created. Each time that the
function is evaluated, the (local to TRICK) word FOO is set
to the value of an expression that modified ~HERBIE~. Since
the third element of TRICK's body is a reference to ~HERBIE~,
we will see the effects of those mutations when we look at
TRICK's body.
>> second :trick
== [foo: append [1 1 1] 1]
Since TRICK's body was created by sort-of-copying a block that's
still in CONSOLE-STRUCT-4, we *will*not* see the effects of the
mutations there.
>> console-struct-4
== [trick: func [/local foo] [foo: append [] 1]
]
Since the first element of TRICK's body is a word that has a
different context than the global one, all of this SETting of
that word has no effect on the global FOO as seen below:
>> foo
== [1 "I'm back!"]
We can "tunnel" into that the body of TRICK and see the value
of TRICK's local FOO as follows:
>> get first second :trick
== [1 1 1]
As with our console input modeling above, there's still a chain
of references to ~HERBIE~ so the value of ~HERBIE~ persists.
And, since the body of TRICK is just a block, we can do block
operations on it:
>> poke second :trick 3 [2]
== [foo: append [2] 1]
Now the body of FOO no longer contains a reference to ~HERBIE~
because I POKEd a different value into the place where that
reference to ~HERBIE~ used to be.
However, there's still another reference to ~HERBIE~
>> get first second :trick
== [1 1 1]
But look what happens when I pull another TRICK ...
>> trick
== [2 1]
>> second :trick
== [foo: append [2 1] 1]
>> get first second :trick
== [2 1]
I've killed ~HERBIE~ !!! (Good thing he was only a virtual name,
or I'd be arrested! ;-)
Now both the (local to TRICK) word FOO and the third element of
TRICK's body refer to a new block. OBTW, that block was
created when I typed the string "poke second :trick 3 [2]" into
the console and REBOL LOADed it. It was then mutated when the
body of TRICK was evaluated.
If you've read this far, you deserve an Olympic medal for the
marathon!!!
The reason for using COPY in front of a "literal" block inside
a REBOL function would be to prevent mutations through a
reference to that block from persisting -- i.e. you want a fresh
instance of that block's content every time.
I know that the above discussion was painful and laborious to
read, but I hope it makes clear that there's already some COPYing
going on. Knowing *when* the COPYing happens and *what* values
are COPYed makes a lot of difference IMHO.
As for CLEAR, it simple discards the content of a series, but
doesn't replace the series itself.
>> foo
== [1 "I'm back!"]
>> oof
== [1 "I'm back!"]
>> clear foo
== []
>> oof
== []
so that OOF and FOO still both refer to the same series, its just
that a (particularly severe!) mutation to that series's value
occurred.
The consequence of THAT fact is illustrated with these two little
tweedles
:
>> dee: func [/local foo] [foo: append copy [] 1]
>> dum: func [/local foo] [foo: append clear [] 1]
I hope that I've belabored my model to the point that all of the
following now make sense to you, most admirable and persistent
reader!
>> a: dee
== [1]
>> b: dee
== [1]
>> c: dum
== [1]
>> d: dum
== [1]
>> append a 2
== [1 2]
>> b
== [1]
>> second :dee
== [foo: append copy [] 1]
>> append c 2
== [1 2]
>> d
== [1 2]
>> second :dum
== [foo: append clear [1 2] 1]
>> e: dee
== [1]
>> f: dum
== [1]
>> second :dum
== [foo: append clear [1] 1]
Or, if you'll pardon the pun, I hope that it's all CLEAR now!
-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" ;
[25/25] from: g:santilli:tiscalinet:it at: 14-Feb-2002 16:02
At 15.52 12/02/02, you wrote:
>What kinds of problems are you referring to?
With CLEAR:
>> a: to-charblock "123"
== [#"1" #"2" #"3"]
>> b: to-charblock "456"
== [#"4" #"5" #"6"]
>> a
== [#"4" #"5" #"6"]
which I think is not what Jason would expect.
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted