22
\$\begingroup\$

There are quite a large number of existing implementatons of FizzBuzz but I awoke this morning in a cold sweat with the terrible revelation that Code Review had no FizzBuzz implementation in Forth! To redress this terrible oversight, I wrote this while sipping my first cup of coffee.

: FIZZBUZZ ( -- )
  CR 100 1 DO 
     I 3 MOD 0= I 5 MOD 0= 2DUP AND IF 
        ." fizzbuzz " DROP DROP
     ELSE IF 
        ." buzz " DROP
     ELSE IF 
        ." fizz " 
     ELSE I . THEN THEN THEN
  LOOP ;

I'm interested in a general review, including possible alternatives to the use of MOD, and whether further factoring should be used. For those unfamiliar with Forth, try http://www.forth.org/tutorials.html

\$\endgroup\$
  • 1
    \$\begingroup\$ This looks familiar. Why does this look familiar? Why do I recognize this language? Found it. Evolve used KForth. \$\endgroup\$ – RubberDuck Jul 29 '14 at 4:41
8
+100
\$\begingroup\$

Are you using two or three spaces of indentation? Yes.

Are you using two and three spaces of indentation? Yes.

Are you using two xor three spaces of indentation? No.

I don't know the coding conventions of Forth, but I'd personally prefer to see using four spaces (one tab) of indentation. Most importantly however, be consistent. </nitpicks> As far as I understand the Forth style conventions, three spaces should be used as indentation always.


I 3 MOD 0= I 5 MOD 0= 2DUP AND IF 

This line is excessively long and contains a lot of different instructions, in my opinion this is unnecessarily confusing. By splitting it up I consider it easier to read and understand.

It can easily be split up to the following:

I 3 MOD 0=
I 5 MOD 0=
2DUP AND
IF

: FIZZBUZZ ( -- )
  CR 100 1 DO 

I'm not quite sure why you print a new line (CR) at the beginning of your function (sorry, your "word"). IMO it's better to print a new line after each iteration.


It would be easy to extract the 100 to be a parameter to your word instead of hardcoding it into it. That would give some flexibility at least, even though most Fizzbuzz challenges only goes to 100. This can easily be accomplished by removing the 100 at the beginning of your word.

Speaking of 100. Your current implementation actually excludes the number 100. This is easily fixed by adding 1 + to the beginning of your word.


Your nested if-else-if-else-if-else-then-then-then is hard to follow when you're not used to how Forth works (yes, I am speaking from experience here). By only changing the indentation of it, it gets easier to follow along with it.

Although it still may look a bit strange that the THEN is at the end, but that's not anything you can do something about :)


With the suggestions above, the code would look like this:

: FIZZBUZZ ( u -- )
    1 + 1 DO 
        I 3 MOD 0=
        I 5 MOD 0=
        2DUP AND IF 
            ." fizzbuzz" DROP DROP
        ELSE
            IF 
               ." buzz" DROP
            ELSE
                IF 
                    ." fizz" 
                ELSE
                    I .
                THEN
            THEN
        THEN
        CR
    LOOP
;

As for alternatives to using mod, even though it is possible to use two separate counters and decrease them and reset them once they hit zero, I don't think that would improve the code much.

Considering that I managed to understand your code and learn a bit of Forth, I'd say that's a compliment to your code. I believe that with some of the changes I've suggested here, it would be much easier for new Forth programmers to understand it.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the review. See forth.org/forth_style.html for an example of Forth coding conventions, although I'm actually using a somewhat newer style. In Forth, it's common practice to have a line end with IF. Also, your stack comment should be modified to ( u -- ). \$\endgroup\$ – Edward Nov 23 '14 at 15:28
  • 4
    \$\begingroup\$ "this line is very confusing for those who have never read or written Forth before" is that a fair standard? Before I learned it, most Python looked like gibberish to me. \$\endgroup\$ – raptortech97 Nov 23 '14 at 17:41
  • \$\begingroup\$ @raptortech97 Edited that part. \$\endgroup\$ – Simon Forsberg Nov 23 '14 at 20:36
0
\$\begingroup\$

This answer by Gerry is taken from comp.lang.forth and avoids the use of mod, using bit vectors instead.

hex
1249 constant 3vec
0421 constant 5vec
4000 constant probe
3vec 5vec or constant 3or5vec
decimal

: bang
   cr 0 101 1
   do
      ?dup 0= if probe then
      3or5vec over and 0= if i . then
      3vec over and if ." fizz" then
      5vec over and if ." buzz" then
      1 rshift cr
   loop
   drop
;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.