append
[1/6] from: fantam::mailandnews::com at: 14-Aug-2000 17:01
Hello all,
In the following script, 'list-dirs is a function that returns the
directory tree under the directory specified as an argument.
On WinNT, using REBOL/Core 2.4.24.3.1, that script will throw an error
when it encounters a directory that has a dot in its name (for
example, "temp.old"). Furthermore, the error is thrown only if that
directory is a second level sub-directory of the argument (for example
%/c/temp/temp1/temp.old
and not "%/c/temp/temp.old").
The most confusing thing is that if I remove the local definition of
'dirtree in the func spec, no error is thrown at all.
I do not understand what is happening.
REBOL []
dir: %/c/temp/
count: 0
list-dirs: func [
"returns dir tree as block"
dir [file!] "root dir"
/local dirtree [block!]
]
[
if count = 0 [dirtree: make block! 100]
count: count + 1
foreach name read dir [
if dir? dir/:name [
append dirtree dir/:name
list-dirs dir/:name
]
]
dirtree
]
print list-dirs dir
halt
--
Fantam
[2/6] from: rebol:techscribe at: 14-Aug-2000 10:42
Hi Fantam,
you're shooting yourself in the foot.
1. The solution
>> list-dirs: func [ dir [file!] "root dir" /local dirtree ] [
[ if count = 0 [dirtree: make block! 100]
[ count: count + 1
[ print ["Count: " count "type? dirtree" type? dirtree]
[ foreach name read dir [
[ if dir? dir/:name [
[ append dirtree dir/:name
[ list-dirs dir/:name
[ ]
[ ]
[ dirtree
[ ]
>>
>> list-dirs %.
Count: 1 type? dirtree block
Count: 2 type? dirtree none
** Script Error: append expected series argument of type: series port.
** Where: append dirtree dir/:name
list-dirs dir/:name
I modified your function slightly. Most importantly I added a print
statement before the foreach loop:
print ["Count: " count "type? dirtree" type? dirtree]
As you can see this print statement reports that upon the first recursive
call of list-dirs (count = 2), dirtree is set to none, it is not a block:
>> list-dirs %.
Count: 1 type? dirtree block
Count: 2 type? dirtree none
Therefore you get an error message, complaining that REBOL cannot append to
a none value:
** Script Error: append expected series argument of type: series port.
** Where: append dirtree dir/:name
a) By making count a global word and setting its value to 1 in list-dirs,
you are preventing dirtree from being set to a block value each time
list-dirs is called.
b) By making dirtree a local word you force it to be initialized to none at
each call to list-dirs, also during recursive calls.
So, when list-dirs is entered recursively, dirtree is set to none by REBOL,
because it is declared a local word (the [block!] expression behind the
word does not have any effect, this notation is only effective for
parameters, but not for local words), count was previously set to 1, and
therefore dirtree is not being set to a block. That generates the error
message documented above.
2. A few points
you wrote:
>script will throw an error
1. Note what REBOL reports as an error. Sometimes that information can be
helpful!
>when it encounters a directory that has a dot in its name (for
>example, "temp.old"). Furthermore, the error is thrown only if that
>directory is a second level sub-directory of the argument (for example
>"%/c/temp/temp1/temp.old" and not "%/c/temp/temp.old").
2. Does the following code work?
>> x: []
>> name: %a.b/
>> dir: %c/d/e.f/
>> append x dir/:name
== [%c/d/e.f/a.b/]
>The most confusing thing is that if I remove the local definition of
>'dirtree in the func spec,
3. What's the [block!] doing after dirtree?
>> f: func [/local dirtree [block!]] [ print type? dirtree]
>> f
none
It certainly has no influence on the type of dirtree!
Hope this helps,
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[3/6] from: galtbarber:mailandnews at: 14-Aug-2000 14:28
Hola, Fantam!
/local dirtree [block!]
as far as I know, you only get to declare the
type of params passed to be checked, not
the type of a local var. as somebody put it,
values are type-checked, not variables,
so it is even more reliable.
Here is the error I got when I ran what you posted:
** Script Error: append expected series argument of type: series port.
** Where: append dirtree dir/:name
list-dirs dir/:name
It appears that on the second call, when
count = 1, then the local dirtree is not initialized,
and Rebol does not have a type for its unset value.
Therefore it complains about the type not being series value.
Obviously, if you remove the local declaration of dirtree,
then a global one works better. It even accumulates all
the whole tree.
But this one works without needing any global variables:
get-dirtree: func [
"returns directory tree as block"
startdir [file!] "starting directory"
/local
dirtree
recursive-dirlist
][
dirtree: make block! 100
recursive-dirlist: func [
dir [file!]
][
foreach name read dir [
if dir? dir/:name [
append dirtree dir/:name
recursive-dirlist dir/:name
]
]
]
recursive-dirlist startdir
dirtree
]
dir: %/c/temp/
print get-dirtree dir
This is a relatively simple example,
but I have often found that it is better
to separate the pure recursive function from
the setup stuff that is actually invoked
during normal usage.
This simple function doesn't really require much
setup, though, so you can just do this
if you want a pure-recursive:
get-dirtree: func [
"returns directory tree as block"
dir [file!] "starting directory"
dirtree [block!]
][
foreach name read dir [
if dir? dir/:name [
append dirtree dir/:name
get-dirtree dir/:name dirtree
]
]
dirtree
]
dir: %/c/temp/
print get-dirtree dir copy []
---------------------------------
You see, you just have to always remember to
initialize and pass in the empty result block.
-Galt
[4/6] from: rebol:techscribe at: 14-Aug-2000 15:10
Hi Galt,
a few minor comments:
you wrote:
[snipped correct and useful insights]
>It appears that on the second call, when
>count = 1, then the local dirtree is not initialized,
Actually, local words are initialized to the value none (as are refinements):
>> f: func [/local word] [ print word ]
>> f
none
>and Rebol does not have a type for its unset value.
The type of unset words is unset!:
>> type? ;- there ain't nothin' here for type?
== unset!
>Therefore it complains about the type not being series value.
Therefore: i.e. because the value of dirtree is none, and none's type is
none!, which is not one of the types required by append.
If the word was indeed unset, then you would get a different error:
>> f: func [/local word] [ unset 'word append word "something to append" ]
>> f
** Script Error: word has no value.
** Where: append word "something to append"
Hope this helps.
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
[5/6] from: fantam:mailandnews at: 16-Aug-2000 18:31
I see. Thanks.
Also, I thought that when you create a word within the body of a function without
declaring it as local, it would still be local, and not a global one,
as it really is.
daniel
> Hi Fantam,
> you're shooting yourself in the foot.
<<quoted lines omitted: 69>>
> http://www.REBOLpress.com
> visit me at http://www.TechScribe.com
--
Fantam
[6/6] from: rebol:techscribe at: 16-Aug-2000 10:35
Hi Daniel,
you wrote:
>Also, I thought that when you create a word within the body of a function
without
>declaring it as local, it would still be local, and not a global one,
>as it really is.
You're right. A word declared in the body of a function is indeed global,
unless it is declared /local or declared local to a use context.
If this was not so, we would need a /global flag instead of a /local flag
for function declarations.
In contrast, a word created in the body of an object using the set-word!
notation (a: "some value") is local to the object's context. To create
global words from within an object context use set (set 'a "some value").
o: make object! [
a: "this word is local to the object referenced by o."
set 'b "this word is global."
]
Hope this helps,
;- Elan [ : - ) ]
author of REBOL: THE OFFICIAL GUIDE
REBOL Press: The Official Source for REBOL Books
http://www.REBOLpress.com
visit me at http://www.TechScribe.com
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted