So there's a silly problem called "FizzBuzz" which is stated very simply:
Write a program that prints the numbers from 1 to 100.
But for multiples of three print "Fizz" instead of the number
....and for the multiples of five print "Buzz".
For numbers which are multiples of both three and five print "FizzBuzz".
The claim is that as easy as this problem sounds, many programmers who walk into interviews have trouble with it. On the c2 programming wiki, someone argues for why this might be the case:
"I think Fizz-Buzz is "hard" for some programmers because (#1) it doesn't fit into any of the patterns that were given to them in school assignments, and (#2) it isn't possible to directly and simply represent the necessary tests, without duplication, in just about any commonly-used modern programming language."
But the design of Ren-C has more than enough chops for this problem! It's particularly elegant using a recent change to DELIMIT, including its specializations like SPACED and UNSPACED.
So how about that FizzBuzz?
count-up n 100 [
print [
unspaced [
if n mod 3 = 0 ["Fizz"]
if n mod 5 = 0 ["Buzz"]
] else [n]
]
]
Presto...that's all it takes!!! UNSPACED will return null if everything in its body opts out, which is the cue for the generic ELSE to run. This brings the magic of being able to avoid duplication in the tests. It's like every piece of problem specification corresponds to just one word in the program!
Love it. FizzBuzz is indeed a neat example of a language challenge.
I'm still inexperienced with unspaced, so its use here seems like a fancy trick but not an intuitive function name.
This is in no way a complaint , I'm just thinking that someone coming from another language might see this short eloquent example but wonder "Cool-- Wait, what the heck is unspaced?".
I guess it seems fairly sensible to to me...especially when people get introduced to it as print unspaced [...]. If that's how you learn it, you then think of being able to lift out the unspacing behavior you used when you needed it into a function. And you can say print spaced [...] too, though that's usually redundant.
Though if it were just JOIN it might appear clearer in this particular case, I think reserving JOIN for something else makes sense. Trying to use a generic series-joining operator for string production is problematic, as @Brett explains in detail.
But it's a good point that presenting FizzBuzz probably needs a little lead-in to explain UNSPACED to the uninitiated, to make absorption smoother.
Yes, I must relearn some of my old ways... I still think in terms of the JOIN/REJOIN vernacular. JOIN is a nice verb which intuitively suggests its purpose. UNSPACED on the other hand, seems defined in contrast to its opposite, which is also less obvious. To a dim bulb like me, anyway. Still not a complaint; I think with more use of REN-C I won't blink at some of the nuanced changes.
This seems like a good example to put on whatever homepage is eventually made for the project.
I knew about spaced/unspaced, had not seen it in action yet. I think unspaced here was very intuitive, like it very much, it is a true Rebol way solution. (And totally unmatched anywhere else afaik).
There's a new tool in the arsenal: the TOKEN! (a.k.a. "issuechar!") which does not participate in delimiting in DELIMIT-based primitives.
Can it help with FizzBuzz?
The UNSPACED is used as an aggregator for the ELSE to trigger on. We can reshape it to trigger off the PRINT's output...but then we'd need another PRINT:
count-up n 100 [
print [
if n mod 3 = 0 [#Fizz]
if n mod 5 = 0 [#Buzz]
] else [
print [n]
]
]
Well...it's different. It trades the potential confusion of what "unspaced" means for the semantics of unspaced TOKEN! in PRINT, and repeating PRINT twice. You lose a level of indentation--that's nice.
But I definitely like how PRINT can be opted out of completely...including the newline...and can telegraph its opt-out-edness to another construct. The fact that you can do this with two PRINTs is interesting in its own right.
Just because we can, I'll point out soft-quoted branching making this lighter (at the expense of comprehensibility, but improving space/speed by removing two series allocations/nested-evaluations)
count-up n 100 [
print [
if n mod 3 = 0 '#Fizz
if n mod 5 = 0 '#Buzz
] else [
print [n]
]
]
As we further chase down essentialism, I think the definition of FOR is going to be arity-3, and INTEGER! semantics would be COUNT-UP. Plus @ lets us print any value, so you could write:
for n 100 [
print [
if n mod 3 = 0 '#Fizz
if n mod 5 = 0 '#Buzz
] else [
print @n
]
]
But if I were making the poster today, it would probably say:
for n 100 [
print [
if n mod 3 = 0 [#Fizz]
if n mod 5 = 0 [#Buzz]
] else [
print [n]
]
]
Or even clearer:
for n [1 thru 100] [
print [
if n mod 3 = 0 [#Fizz]
if n mod 5 = 0 [#Buzz]
] else [
print [n]
]
]
This helps you know precisely whether it goes up-to-and-including a hundred (1 thru 100) or up-to-but-not-including a hundred (1 to 100), and that it starts at 1 and not 0.
But to be frank, I do like the 'count-up version better. It makes the language stand out a little more from all the other languages providing their 'for loop constructions.