This works in gforth. I'm not sure if it's valid ANS Forth, since it
requires multiple s" ..." string literals to be alive at the same time.
Coding and testing wasn't difficult in absolute terms, since the program
is so small, but it still took multiple tries and head-scratching, and
the result seems unmodular compared to a Python version. Maybe there
was something I could have done better.
: fb0 ( flag sa su n d -- flag )
mod 0= if type -1 else 2drop 0 then or ;
: fb ( n -- flag ) >r
0 ( flag )
s" Fizz" r@ 3 fb0
s" Buzz" r@ 5 fb0
s" Zoom" r> 7 fb0 ;
: f ( n -- ) dup fb ( flag ) 0= if . else drop then cr ;
: run ( -- ) \ run to 120 so that 3*5*7=105 is included
120 1 do i f loop ;
Paul Rubin <no.em...@nospam.invalid> writes Re: Fizz Buzz Zoom
[..]
> This works in gforth. I'm not sure if it's valid ANS Forth, since it
> requires multiple s" ..." string literals to be alive at the same time.
> Coding and testing wasn't difficult in absolute terms, since the program
> is so small, but it still took multiple tries and head-scratching, and
> the result seems unmodular compared to a Python version. Maybe there
> was something I could have done better.
Unmodular? As there is no firm specification, go for the obvious:
\ ?AT ( -- column row ) tests cursor position
: 3/5/7/n? ( n -- )
#120 1 DO
CR DUP 3 MOD 0= IF ." Fizz" ENDIF
DUP 5 MOD 0= IF ." Buzz" ENDIF
DUP 7 MOD 0= IF ." Zoom" ENDIF ?AT DROP 0= IF . ELSE DROP ENDIF LOOP ;
m...@iae.nl (Marcel Hendrix) writes:
> Unmodular? As there is no firm specification, go for the obvious:
> \ ?AT ( -- column row ) tests cursor position
I don't think relying on the cursor position is especially modular.
Also, it extends reasonably well to n=11:
DUP 11 MOD 0= IF ." Blarg" ENDIF
but fails for n=13, which is an unlucky number and should therefore
print as the empty string.
Don't think there's anything wrong with your solution.
Here's another one I threw together just now:
: divisible-by? ( n1 n2 -- f f ) \ is n1 divisible by n2?
mod 0= dup ;
: fizz ( n -- f ) \ true if divisible
3 divisible-by? if ." Fizz" then ;
: buzz ( n -- f ) \ true if divisible
5 divisible-by? if ." Buzz" then ;
: zoom ( n -- f ) \ true if divisible
7 divisible-by? if ." Zoom" then ;
: run ( n -- )
1 do
cr i fizz
i buzz or
i zoom or
0= if i . then
loop ;
0 [if]
Comments:
- perhaps more meaningful names
- no need for return stack for keeping i
- a bit less stack thrashing, just 1 dup
- fewer items being tossed around on the stack
- fizz/buzz/zoom could be factored further, not sure it's worth it
No doubt someone else has a more clever solution.
[then]
Paul Rubin <no.em...@nospam.invalid> writes Re: Fizz Buzz Zoom
> m...@iae.nl (Marcel Hendrix) writes:
>> Unmodular? As there is no firm specification, go for the obvious:
>> \ ?AT ( -- column row ) tests cursor position
> I don't think relying on the cursor position is especially modular.
You mean portable?
> Also, it extends reasonably well to n=11:
> DUP 11 MOD 0= IF ." Blarg" ENDIF
> but fails for n=13, which is an unlucky number and should therefore
> print as the empty string.
I don't know anything about 13. Your program is my specification.
But I should have tested better. Here it is again with Blargh, 11 and 13.
: 3/5/7/11/n? ( -- )
#120 1 DO
CR i 3 MOD 0= IF ." Fizz" ENDIF
i 5 MOD 0= IF ." Buzz" ENDIF
i 7 MOD 0= IF ." Zoom" ENDIF i #11 MOD 0= IF ." Blargh" ENDIF ?AT DROP 0=
I #13 <> AND IF i . ENDIF LOOP ;
Doug Hoffman <glide...@gmail.com> writes:
> - perhaps more meaningful names
> - no need for return stack for keeping i
> - a bit less stack thrashing, just 1 dup
> - fewer items being tossed around on the stack
> - fizz/buzz/zoom could be factored further, not sure it's worth it
The fizz/buzz/zoom design seems intended to make the the whole
multi-level program to have the right stuff on the stack all the time,
which is in the Forth spirit, but the lack of factoring doesn't seem
ideal. More factoring would seem to require passing a lot of args to
the bottom level, like my example did.
My version originally used gforth locals instead of the return stack
stuff, but that seemed like overkill.
> Doug Hoffman <glide...@gmail.com> writes:
>> - perhaps more meaningful names
>> - no need for return stack for keeping i
>> - a bit less stack thrashing, just 1 dup
>> - fewer items being tossed around on the stack
>> - fizz/buzz/zoom could be factored further, not sure it's worth it
> The fizz/buzz/zoom design seems intended to make the the whole
> multi-level program to have the right stuff on the stack all the time,
> which is in the Forth spirit, but the lack of factoring doesn't seem
> ideal. More factoring would seem to require passing a lot of args to
> the bottom level, like my example did.
I was thinking of something like this:
: divisible-by? ( n1 n2 -- f )
mod 0= ;
: speak? { n1 n2 str -- f }
n1 n2 divisible-by? dup if str count type then ;
: fizz ( n1 -- f )
3 c" Fizz" speak? ;
: buzz ( n1 -- f )
5 c" Buzz" speak? ;
: zoom ( n1 -- f )
7 c" Zoom" speak? ;
: run ( n -- )
1 do
cr i fizz
i buzz or
i zoom or
0= if i . then
loop ;
I'm not sure the extra factoring was worthwhile, but there it is.
> My version originally used gforth locals instead of the return stack
> stuff, but that seemed like overkill.
I will use locals with no apologies when they suit my purpose, as you can see above.
Doug Hoffman <glide...@gmail.com> writes:
> I'm not sure the extra factoring was worthwhile, but there it is.
I see, you got rid of one of the bottom level stack items by using
counted strings instead of addr/u, and got rid of another one by
repeating the "or" after each top level divisor check. I guess both are
reasonable.
> I will use locals with no apologies when they suit my purpose, as you
> can see above.
Using them does make stuff easier. I wonder whether typical embedded
Forths support them.
On Sunday, October 7, 2012 10:18:58 PM UTC+3, Paul Rubin wrote:
> m...@iae.nl (Marcel Hendrix) writes:
> >> I don't think relying on the cursor position is especially modular.
> > You mean portable?
> I mean separating compuational from i/o concerns, that sort of thing.
Hi Paul!
A-solution tests + :)
variable offs
: PAD+ ( ca u -- )
PAD offs @ tuck + swap 80 swap -
rot umin dup offs +! move ;
: i/o PAD offs @ type space ;
: main ( n -- )
1 DO
0 dup offs !
I 3 mod 0= IF s" fizz" PAD+ -1 OR THEN
I 5 mod 0= IF s" buzz" PAD+ -1 OR THEN
I 7 mod 0= IF s" zoom" PAD+ -1 OR THEN
IF I 0 <# #S #> PAD+ i/o THEN LOOP ;
\ Tests
s" alfa" PAD+
s" beta" PAD+
PAD offs @ type
cr i/o cr cr 120 main cr cr .s cr bye
> Doug Hoffman <glide...@gmail.com> writes:
>> I'm not sure the extra factoring was worthwhile, but there it is.
> I see, you got rid of one of the bottom level stack items by using
> counted strings instead of addr/u, and got rid of another one by
> repeating the "or" after each top level divisor check. I guess both are
> reasonable.
>> I will use locals with no apologies when they suit my purpose, as you
>> can see above.
> Using them does make stuff easier. I wonder whether typical embedded
> Forths support them.
Not hard to get rid of locals in this case (often not so easy). Locals are in ANS, btw:
: speak? ( n1 n2 str -- f )
>r divisible-by? dup if r@ count type then r> drop ;
or maybe just cram the whole thing together, perhaps the obvious thing to do for such a simple routine. Doing so for more complex code is usually a mistake, IMO. A personal style thing perhaps:
: run'
120 1 do
cr i 3 mod 0= dup if ." Fizz" then
i 5 mod 0= dup if ." Buzz" then or
i 7 mod 0= dup if ." Zoom" then or
0= if i . then
loop ;
> : run'
> 120 1 do
> cr i 3 mod 0= dup if ." Fizz" then
> i 5 mod 0= dup if ." Buzz" then or
> i 7 mod 0= dup if ." Zoom" then or
> 0= if i . then
> loop ;
> -Doug
At last! Somebody posted what I was thinking. I was looking at the
everyone's code thinking "Hmmm... I'm clearly not getting this"
Paul Rubin <no.em...@nospam.invalid> writes:
>This works in gforth. I'm not sure if it's valid ANS Forth, since it
>requires multiple s" ..." string literals to be alive at the same time.
You only use the compilation semantics of S", and the strings from
that live forever, so no need to worry about that.
>Coding and testing wasn't difficult in absolute terms, since the program
>is so small, but it still took multiple tries and head-scratching, and
>the result seems unmodular compared to a Python version. Maybe there
>was something I could have done better.
What is the specification? What is the Python version?
The original question only used "Fizz" and "Buzz" and the most common
solution involves dealing with the case n%15==0 separately from the
n%3==0 and n%5==0 cases, which I thought was ugly. There is not a
canonical Python version. There's a discussion here of a bunch of
Haskell and Python solutions that I mostly didn't like, because of too
much abstraction (the Haskell monoid versions), or not enough (the
Python version everyone liked handles the empty string incorrectly):
My preferred version separates the code from the data ("figure out the
data structure, and the code takes care of itself"). First write down
the data:
spec = [(3, "Fizz"), (5, "Buzz"), (7, "Zoom")]
The following code then flows pretty easily (be aware that ''.join(...)
is a Python idiom for concatenating a list of strings, and that a
boolean test on a list returns true iff the list is non-empty):
def fb(n):
ns = [s for (d,s) in spec if n%d==0]
return ''.join(ns) if ns else str(n)
for i in xrange(1,121): print fb(i)
This handles arbitrary numbers of divisors and does the right thing if
one or more of the strings is empty. In Haskell I'd probably code it
the right way, or if I did it with monoids I'd use mconcat instead of
that thing with monad comprehensions (don't worry if that makes no
sense).
According to the Wikipedia article about FizzBuzz which I just read last
night, it was originally a drinking game rather than a programming
challenge, and the syllable for divisor 7 is traditionally "Woof" rather
than "Zoom". One learns all kinds of things on the internets.
> The original question only used "Fizz" and "Buzz" and the most common
> solution involves dealing with the case n%15==0 separately from the
> n%3==0 and n%5==0 cases, which I thought was ugly. There is not a
> canonical Python version. There's a discussion here of a bunch of
> Haskell and Python solutions that I mostly didn't like, because of too
> much abstraction (the Haskell monoid versions), or not enough (the
> Python version everyone liked handles the empty string incorrectly):
> My preferred version separates the code from the data ("figure out the
> data structure, and the code takes care of itself"). First write down
> the data:
> spec = [(3, "Fizz"), (5, "Buzz"), (7, "Zoom")]
> The following code then flows pretty easily (be aware that ''.join(...)
> is a Python idiom for concatenating a list of strings, and that a
> boolean test on a list returns true iff the list is non-empty):
> def fb(n):
> ns = [s for (d,s) in spec if n%d==0]
> return ''.join(ns) if ns else str(n)
> for i in xrange(1,121): print fb(i)
> This handles arbitrary numbers of divisors and does the right thing if
> one or more of the strings is empty. In Haskell I'd probably code it
> the right way, or if I did it with monoids I'd use mconcat instead of
> that thing with monad comprehensions (don't worry if that makes no
> sense).
> According to the Wikipedia article about FizzBuzz which I just read last
> night, it was originally a drinking game rather than a programming
> challenge, and the syllable for divisor 7 is traditionally "Woof" rather
> than "Zoom". One learns all kinds of things on the internets.
Thanks, nice anecdote. :-)
Although I'd say that most of our candidates could solve the problem. But perhaps just because we focus on university graduates who still have some fresh math in their heads.
Paul Rubin <no.em...@nospam.invalid> writes:
>an...@mips.complang.tuwien.ac.at (Anton Ertl) writes:
>> What is the specification? What is the Python version?
>It's a version of the famous FizzBuzz interview question:
>The original question only used "Fizz" and "Buzz" and the most common
>solution involves dealing with the case n%15==0 separately from the
>n%3==0 and n%5==0 cases, which I thought was ugly.
Ok, that specification is:
|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".
Given the separate case in the spec for Fizz Buzz, having a separate
case in the program looks pretty natural to me.
Anyway, what is the specification of Fizz Buzz Zoom?
an...@mips.complang.tuwien.ac.at (Anton Ertl) writes:
> Given the separate case in the spec for Fizz Buzz, having a separate
> case in the program looks pretty natural to me.
Since you've already done both tests (mod 3 and mod 5), doing them again
has a DRY problem.
> |multiples of five print "Buzz". For numbers which are multiples of
> |both three and five print "FizzBuzz". ...
> Anyway, what is the specification of Fizz Buzz Zoom?
Same thing, except additionally, if n is divisible by 7, print "Zoom".
So for example, for n=21 you'd print FizzZoom, for n=35 you'd print
BuzzZoom, and for n=105 you'd print FizzBuzzZoom.
It could be treated as a two-part question:
1) solve FizzBuzz
2) extend FizzBuzz solution to handle Zoom.
> This works in gforth. I'm not sure if it's valid ANS Forth, since it
> requires multiple s" ..." string literals to be alive at the same time.
> Coding and testing wasn't difficult in absolute terms, since the program
> is so small, but it still took multiple tries and head-scratching, and
> the result seems unmodular compared to a Python version. Maybe there
> was something I could have done better.
> : fb0 ( flag sa su n d -- flag )
> mod 0= if type -1 else 2drop 0 then or ;
Paul Rubin <no.em...@nospam.invalid> writes:
>an...@mips.complang.tuwien.ac.at (Anton Ertl) writes:
>> Given the separate case in the spec for Fizz Buzz, having a separate
>> case in the program looks pretty natural to me.
>Since you've already done both tests (mod 3 and mod 5), doing them again
>has a DRY problem.
What's a "DRY problem"?
>> |multiples of five print "Buzz". For numbers which are multiples of
>> |both three and five print "FizzBuzz". ...
>> Anyway, what is the specification of Fizz Buzz Zoom?
>Same thing, except additionally, if n is divisible by 7, print "Zoom".
>So for example, for n=21 you'd print FizzZoom, for n=35 you'd print
>BuzzZoom, and for n=105 you'd print FizzBuzzZoom.
Now maybe someone should write a blog posting about writing
specifications. Let me have a go at that. What you mean is probably:
Write a program that prints the numbers from 1 to 121. 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". For numbers which are multiples
of seven, print "Zoom". For numbers which are multiples of three and
seven, print "FizzZoom". For numbers which are multiples of five and
seven, print "BuzzZoom". For numbers which are multiples of three,
five and seven, print "FizzBuzzZoom".
Why did I write it that way? Because it's closest to the FizzBuzz
spec and also the way you wrote the incomplete spec for FizzBuzzZoom.
That spec naturally leads to a solution you consider "ugly". Now
maybe someone can write a less ugly specification, and maybe that
naturally leads to a less ugly program.
Anton Ertl wrote:
> That spec naturally leads to a solution you consider "ugly". Now
> maybe someone can write a less ugly specification, and maybe that
> naturally leads to a less ugly program.
The only ugly think is that you somehow have to track your decision to prevent printing the number. A slight variation of Doug Hoffman's code doesn't look that ugly.
: mod? ( flag i n -- flag' flagp )
mod 0= tuck or swap ;
: fizzbuzzzoom ( n -- ) 1 ?DO false
i 3 mod? IF ." Fizz" THEN
i 5 mod? IF ." Buzz" THEN
i 7 mod? IF ." Zoom" THEN
0= IF i 0 .r THEN space \ or cr, if you prefer
LOOP ;
If you think this is not enough DSL, try this:
: mod? ( flag i n -- flag' flagp )
mod 0= tuck or swap ;
: ?" ( flag n "string"<"> -- flag' )
]] i swap mod? IF ." THEN [[ ; immediate
: fizzbuzzzoom ( n -- ) 1 ?DO false
3 ?" Fizz" 5 ?" Buzz" 7 ?" Zoom"
0= IF i 0 .r THEN space \ or cr, if you prefer
LOOP ;
-- Bernd Paysan
"If you want it done right, you have to do it yourself"
http://bernd-paysan.de/
an...@mips.complang.tuwien.ac.at (Anton Ertl) writes:
> What's a "DRY problem"?
DRY = Don't Repeat Yourself. Basically, "this should be factored some
more".
> Write a program that prints the numbers from 1 to 121. 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". For numbers which are multiples
> of seven, print "Zoom". For numbers which are multiples of three and
> seven, print "FizzZoom". For numbers which are multiples of five and
> seven, print "BuzzZoom". For numbers which are multiples of three,
> five and seven, print "FizzBuzzZoom".
This seems like a messy specification to me, and also an incorrect or
ambiguous one, since (e.g.) for n=15 it allows printing "Fizz Buzz
FizzBuzz". Its size also grows exponentially in the number of divisors.
Here's another attempt:
Write a program that prints the numbers from 1 to 120 (inclusive) in
FBZ (Fizz-Buzz-Zoom) notation, one per line. For a natural number n
that is coprime to 3, 5, and 7, the FBZ notation for n is the same as
the decimal representation of n. For other natural numbers n, the
FBZ notation for n is the concatenation of the F, B, and Z notations
for n. The F notation for natural n is the string "Fizz" if n is a
multiple of 3, otherwise it is the empty string. The B notation for
natural n is the string "Buzz" if n is a multiple of 5, otherwise it
is the empty string. The Z notation for natural n is the string
"Zoom" if n is a multiple of 7, otherwise it is the empty string.
That is also a messy spec, but most ways I see to clean it up involve
introducing more machinery that in other ways makes it worse. I do
think the monoid implementation
fbz n = fromMaybe (show n) (mconcat [f 3 "Fizz", f 5 "Buzz", f 7 "Zoom"])
where f d s = if n`mod`d==0 then Just s else Nothing
main = map (putStrLn . fbz) [1..120]
is the most conceptually explicit reflection of how I see the problem,
even though in actual code I like the list-based version better due to
it having fewer implementation artifacts (library dependencies etc.)
Anyway, as someone in the earlier clf thread mentioned, it's an
interview question rather than a "program from spec" problem. In that
situation, some ambiguity can be a good thing since it lets you see the
candidate's approach to resolving it.
There is a formal specification language called Z-notation (nothing to
do with the "Z notation" above) that some Ada projects use. Now I'm
sort of wondering what a Z-notation spec for this problem would look
like.
> That spec naturally leads to a solution you consider "ugly". Now
> maybe someone can write a less ugly specification, and maybe that
> naturally leads to a less ugly program.
Even with a formal spec, it's still appropriate to identify the patterns
inherent in the spec and reflect them in the code.
On 2012-10-08, Paul Rubin <no.em...@nospam.invalid> wrote:
> Anyway, as someone in the earlier clf thread mentioned, it's an
> interview question rather than a "program from spec" problem. In that
> situation, some ambiguity can be a good thing since it lets you see the
> candidate's approach to resolving it.
Indeed. This is interview test. There are many ways to answer it. It gives idea what kind of code applicant writes and it reveals how applicant handdles problems.
I was given this kind of question after a short interview. The task was given on paper "Solve this problem." and there was the place to paper code it. I had said that I knew some forth so I solved it quite fast with forth. And I failed.
My program would work. I had even a stack comment for those three words.
What I missed was documenting what the code did, who wrote it and so on.
So far all solutions suffer the same problem. Very good programmers here don't bother to document.