[REBOL] Combinations of things (small puzzler)
From: joel:neely:fedex at: 20-Oct-2001 16:06
I'm a bit puzzled about the difference in behavior between
REPEAT var blockexpression
and
REPEAT var numericexpression
as illustrated by the following little story. It's not clear to me
whether this is an inconsistency or a bug.
I have a small collection of some things:
some-things: ["camel" "lemur" "mouse"]
I'd like to print all possible combinations of three of these things
(allowing replacement between selections).
any3: function [
things [block!]
][
temp
][
temp: array 3
repeat item things [
temp/1: item
repeat item things [
temp/2: item
repeat item things [
temp/3: item
print temp
]
]
]
]
which behaves as expected:
>> any3 some-things
camel camel camel
camel camel lemur
camel camel mouse
camel lemur camel
camel lemur lemur
camel lemur mouse
camel mouse camel
camel mouse lemur
camel mouse mouse
lemur camel camel
lemur camel lemur
lemur camel mouse
lemur lemur camel
lemur lemur lemur
lemur lemur mouse
lemur mouse camel
lemur mouse lemur
lemur mouse mouse
mouse camel camel
mouse camel lemur
mouse camel mouse
mouse lemur camel
mouse lemur lemur
mouse lemur mouse
mouse mouse camel
mouse mouse lemur
mouse mouse mouse
>>
Of course, that function is specialized to picking exactly 3 at a time,
which I can overcome with a little recursion.
anyn: function [
n [integer!] things [block!]
][
temp
][
temp: array n
do .anyn: func [
i [integer!] n [integer!] b [block!]
][
either i > n [
print b
][
repeat item things [
poke b i item
.anyn i + 1 n b
]
]
] 1 n temp
]
which allows me to say
>> anyn 3 some-things
camel camel camel
camel camel lemur
camel camel mouse
... and so forth, until ...
mouse mouse camel
mouse mouse lemur
mouse mouse mouse
>>
Now, REPEAT is supposed to be able to step through either the values
in a block (for a block argument) or the integers 1 through some limit
(for an integer argument). Let's rework ANY3 to use an "indexing"
strategy.
any-3: function [
things [block!]
][
temp len
][
temp: array 3
len: length? things
repeat i len [
temp/1: things/:i
repeat i len [
temp/2: things/:i
repeat i len [
temp/3: things/:i
print temp
]
]
]
]
Now, please notice that -- up to this point -- it hasn't mattered that
each use of REPEAT in a single solution operates on the same word. But
when we try to generalize ANY-3 to make a variable number of selections
from our list of some things ...
any-n: function [
n [integer!] things [block!]
][
temp len .any-n
][
temp: array n
len: length? things
do .any-n: func [
p [integer!] n [integer!] b [block!]
][
either p > n [
print b
][
repeat i len [
poke b p things/:i
.any-n p + 1 n b
]
]
] 1 n temp
]
... we get a surprise!
>> any-n 3 some-things
camel camel camel
camel camel lemur
camel camel mouse
camel none camel
camel none lemur
camel none mouse
camel none camel
camel none lemur
camel none mouse
none camel camel
none camel lemur
none camel mouse
none none camel
none none lemur
none none mouse
none none camel
none none lemur
none none mouse
none camel camel
none camel lemur
none camel mouse
none none camel
none none lemur
none none mouse
none none camel
none none lemur
none none mouse
>>
If we get those pesky things out of the way, and just look at the
indices ...
any-n-dices: function [
n [integer!] things [block!]
][
temp len .any-n
][
temp: array n
len: length? things
do .any-n-dices: func [
p [integer!] n [integer!] b [block!]
][
either p > n [
print b
][
repeat i len [
poke b p i
.any-n-dices p + 1 n b
]
]
] 1 n temp
]
... we see this...
>> any-n-dices 3 some-things
1 1 1
1 1 2
1 1 3
1 4 1
1 4 2
1 4 3
1 4 1
1 4 2
1 4 3
4 1 1
4 1 2
4 1 3
4 4 1
4 4 2
4 4 3
4 4 1
4 4 2
4 4 3
4 1 1
4 1 2
4 1 3
4 4 1
4 4 2
4 4 3
4 4 1
4 4 2
4 4 3
>>
... which shows that there appears to be an interaction between the values
of the controlled word at different levels of recursive evaluation, which
didn't appear to happen when REPEAT was given a block of values. The
obvious work-around is to use an explicit FOR loop (or WHILE) ...
for-any-n-dices: function [
n [integer!] things [block!]
][
temp len .for-any-n
][
temp: array n
len: length? things
do .for-any-n-dices: func [
p [integer!] n [integer!] b [block!]
][
either p > n [
print b
][
for i 1 len 1 [
poke b p i
.for-any-n-dices p + 1 n b
]
]
] 1 n temp
]
... which behaves as expected ...
>> for-any-n-dices 3 some-things
1 1 1
1 1 2
1 1 3
1 2 1
1 2 2
1 2 3
1 3 1
1 3 2
1 3 3
2 1 1
2 1 2
2 1 3
2 2 1
2 2 2
2 2 3
2 3 1
2 3 2
2 3 3
3 1 1
3 1 2
3 1 3
3 2 1
3 2 2
3 2 3
3 3 1
3 3 2
3 3 3
>>
... but this certainly violates the expectation that ...
REPEAT var number
... and ...
FOR var 1 number 1
... are equivalent REBOL phrases.
Sooooooo... Have I missed something in the documentation somewhere? Note
that the right number of lines get printed in all cases, it's just that
the contents are goofy for recursive invocations of REPEAT with a numeric
second argument.
-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" ;