Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FizzBuzz

809 views
Skip to first unread message

keo...@gmail.com

unread,
Feb 28, 2007, 6:16:19 PM2/28/07
to
Hi,

recently there was a big fuzz on digg.com and the like about this
article:

http://tickletux.wordpress.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/

The author suggests that most self-called programmers can't solve the
following problem:

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".

As I am interested but not yet very proficient in stack based
programming languages, I ask you: how would you solve this problem in
FORTH?

Keo

Coos Haak

unread,
Feb 28, 2007, 7:21:47 PM2/28/07
to
Op 28 Feb 2007 15:16:19 -0800 schreef keo...@gmail.com:

Why are you interested in my solution?
Why don't you give it a try?
Then we'll discuss it further.

--
Coos

CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html

Cesar Rabak

unread,
Feb 28, 2007, 7:42:02 PM2/28/07
to
keo...@gmail.com escreveu:
[snipped]

> As I am interested but not yet very proficient in stack based
> programming languages, I ask you: how would you solve this problem in
> FORTH?
>

Did you study the solution given in the post # 53?

John Passaniti

unread,
Feb 28, 2007, 7:42:34 PM2/28/07
to
keo...@gmail.com wrote:
> As I am interested but not yet very proficient in stack based
> programming languages, I ask you: how would you solve this problem in
> FORTH?

: fizz? 3 mod 0= dup if ." fizz" endif ;
: buzz? 5 mod 0= dup if ." buzz" endif ;

: doit 101 1 do
i fizz?
i buzz?
or 0= if i . endif
cr
loop
;

Andreas Kochenburger

unread,
Mar 1, 2007, 8:29:46 AM3/1/07
to
Hi
did you recently lose your driver's license?

<keo...@gmail.com> schrieb im Newsbeitrag
news:1172704579.4...@z35g2000cwz.googlegroups.com...

Andrew Haley

unread,
Mar 2, 2007, 7:29:39 AM3/2/07
to

The code is over-factored, making it hard to read. There is also too
much stack noise: this problem really doesn't need DUP SWAP etc.

A simple solution is

: bang
100 1 do
i 15 mod 0= if ." FizzBuzz " else
i 3 mod 0= if ." Fizz " else
i 5 mod 0= if ." Buzz " else
i . then then then
loop ;

There are many more complex and efficient solutions possible.

( "the numbers from 1 to 100" is ambiguous. I'm assuming a half-open
interval. )

Andrew.

Doug Hoffman

unread,
Mar 2, 2007, 8:04:01 AM3/2/07
to
On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:

> Cesar Rabak <csra...@yahoo.com.br> wrote:
> > keo...@gmail.com escreveu:
> > [snipped]
> >> As I am interested but not yet very proficient in stack based
> >> programming languages, I ask you: how would you solve this problem in
> >> FORTH?
>
> > Did you study the solution given in the post # 53?
>
> The code is over-factored, making it hard to read. There is also too
> much stack noise: this problem really doesn't need DUP SWAP etc.
>
> A simple solution is
>
> : bang
> 100 1 do
> i 15 mod 0= if ." FizzBuzz " else
> i 3 mod 0= if ." Fizz " else
> i 5 mod 0= if ." Buzz " else
> i . then then then
> loop ;

John Passaniti's solution is a bit cleaner IMO. He factors fizz and
buzz, which eliminates the need for 15 mod.

-Doug

Andrew Haley

unread,
Mar 2, 2007, 8:22:37 AM3/2/07
to

And adds a couple of DUPs and an OR. Shrug.

It's quite a clever solution. There's not much in it, really, and
either would be from my POV perfectly acceptable.

Andrew.

J Thomas

unread,
Mar 2, 2007, 10:48:03 AM3/2/07
to
On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:
> Cesar Rabak <csra...@yahoo.com.br> wrote:
> > keo...@gmail.com escreveu:
> > [snipped]
> >> As I am interested but not yet very proficient in stack based
> >> programming languages, I ask you: how would you solve this problem in
> >> FORTH?
>
> > Did you study the solution given in the post # 53?
>
> The code is over-factored, making it hard to read. There is also too
> much stack noise: this problem really doesn't need DUP SWAP etc.

I don't see any problem with that code. It looks fine to me.

> A simple solution is
>
> : bang
> 100 1 do
> i 15 mod 0= if ." FizzBuzz " else
> i 3 mod 0= if ." Fizz " else
> i 5 mod 0= if ." Buzz " else
> i . then then then
> loop ;

Your solution looks fine to me too.

Your code brings up something for me. I notice those three then then
thens. Forth doesn't usually have a way to skip over the rest of a
loop and then repeat. We have ways to break out of the loop, but not a
command to skip to the end of the loop. So we have ugly things like
then then then to do it, both in DO loops and in BEGIN loops.

I've never felt the need for an improvement there. Multiple THENs
don't actually cause a performance hit, only a very slight compile-
time hit. Wil Baden made his THENS command which palliates it.

Somehow I never noticed until now that you can get a similar result
with multiple BEGINs.

: bang1
0 BEGIN BEGIN 1+ cr
dup 3 mod DUP 0= if ." Fizz" then
over 5 mod dup 0= if ." Buzz" then
or 0= UNTIL
dup .
dup 100 >
UNTIL ;

I certainly don't want to propose this as clear writing or even
adequate code (like, the method would run over if you changed the
limit to 99BEG), it's just that I never thought of this as a single
loop with a continuation. If you had a word that was just like UNTIL
except it left a copy of the dest, it would effectively skip to (but
not past) the end of the loop on failure or execute the rest of the
loop on success.

BEGIN ... SKIP-TO-REPEAT ... SKIP-TO-REPEAT ... SKIP-TO-REPEAT ...
WHILE ... REPEAT

Again, I've never felt the need to have something like this, I've
always just accepted THEN THEN THEN as good enough.

Additional control structure words might someimes make the flow of
control clearer, provided we all remembered what they did. What we
already have is adequate.

: bang2
101 0 do
i 3 mod 0= 1 and
i 5 mod 0= 2 and or
case
0 of i . endof
1 of Fizz endof
2 of Buzz endof
3 of FizzBuzz endof
endcase
loop ;

This one doesn't read better either, but at least the most common
cases are tested the most often. A jump table might be more efficient
but also less readable.

: DUP. dup . ;

CREATE TABLE
' DUP. ,
' Fizz ,
' Buzz ,
' FizzBuzz ,

: do-it
CELLS TABLE + @ EXECUTE ;

: which-action ( n -- n 0|1|2|3 )
dup
i 3 mod 0= 1 and
i 5 mod 0= 2 and or ;

: Bang3
0 begin
1+ dup 101 <
while
which-action do-it
repeat drop ;

I guess when the first methods I learned give a result that isn't too
long, then that's the most readable. There's nothing hard to
understand about nested IF THENs. Just, if they get too complex you
can lose track of how deeply they're nested and which places which
result happens.

Bruce McFarling

unread,
Mar 2, 2007, 11:03:12 AM3/2/07
to
On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:
> A simple solution is

> : bang
> 100 1 do
> i 15 mod 0= if ." FizzBuzz " else
> i 3 mod 0= if ." Fizz " else
> i 5 mod 0= if ." Buzz " else
> i . then then then
> loop ;

Factor it and you'll be there. John Passaniti has a good factoring.

Doug Hoffman

unread,
Mar 2, 2007, 12:34:23 PM3/2/07
to
On Mar 2, 8:22 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:

> And adds a couple of DUPs and an OR. Shrug.

>
> It's quite a clever solution. There's not much in it, really, and
> either would be from my POV perfectly acceptable.

Yes. It's no big deal one way or the other. My first version looked
more like John's and was coded as fast as I could type it (as I
suspect was the case for you and John and most Forthers). But I do
worry a bit when I see triply nested IF-ELSE-THENs.

In the Mops kernel we have always had NIF, which is the same as 0=
IF. I find NIF to be very useful. Using that and after seeing John's
code, my favorite would be the following:

: fizz? ( n -- flag ) \ flag is 0 if hit
3 mod dup NIF ." Fizz" THEN ;

: buzz? ( n -- flag ) \ flag is 0 if hit
5 mod dup NIF ." Buzz" THEN ;

: FizzBuzz
101 1 DO cr
i fizz?
i buzz?
* IF i . THEN
LOOP ;


-Doug

p.s. Wait! I forgot to use objects! I must be coming down with the
flu or something. ;-)

Gerry

unread,
Mar 3, 2007, 9:37:16 AM3/3/07
to

Nothing wrong with other solutions but a simple solution avoiding
nested ifs etc uses bit vectors. The constants can be stuck in line

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
;


Bruce McFarling

unread,
Mar 3, 2007, 10:31:29 AM3/3/07
to

one reason I find this one well factored is that when I tried, I
found it was easy to make the pretty printing as a separate factor:

: fizz? ( n -- fl ) 3 MOD 0= DUP IF ." Fizz" THEN ;
: buzz? ( n -- fl ) 5 MOD 0= DUP IF ." Buzz" THEN ;
: ?.fizzbuzz-WS ( fl1 fl2 -- fl1 fl2 )
2DUP AND IF CR ELSE 2DUP OR IF SPACE THEN THEN
;

: bang1 ( -- ) 101 1 DO
I fizz? I buzz? ?.fizzbuzz-WS
OR 0= IF I . THEN
LOOP
;

J Thomas

unread,
Mar 3, 2007, 11:09:46 AM3/3/07
to
On Mar 3, 9:37 am, "Gerry" <g...@jackson9000.fsnet.co.uk> wrote:

> Nothing wrong with other solutions but a simple solution avoiding
> nested ifs etc uses bit vectors. The constants can be stuck in line
>
> 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
> ;

Very nice! I think I can make that solution more readable, though less
efficient.


variable 5mod
variable 3mod

\ repeated test3 will count to 3 and then start over at 0. Leaves a
true flag every 3rd time.
: test3 ( -- flag )
3mod @ 1+ dup 3 = if drop 0 then dup 3mod ! 0= ;

: test5 ( -- flag )
5mod @ 1+ dup 5 = if drop 0 then dup 5mod ! 0= ;

: bang
0 3mod ! 0 5mod !
101 1 do
cr
test3 dup if ." Fizz" then 0=
test5 dup if ." Buzz" then 0=
and if i . then
loop ;


We could get rid of the if in the tests.

: test3 ( -- flag )
3mod @ 1+ dup 3 = 3 and xor dup 3mod ! 0= ;

We could get rid of the variables.

: test3 ( n -- n' flag )
1+ dup 3 = 3 and xor dup 0= ;

We could factor the test to get rid of the constants.

: testN ( n limit -- n' limit flag )
>r 1+ dup r@ = r@ and xor r> over 0= ;

But it isn't getting more readable.

We can write for size and speed -- for efficiency. But an optimising
compiler might likely do it better. It isn't clear what to optimise,
either. On some processors MOD is slow. On others it isnt. With an
onchip data stack fetching variables might be very slow. Or maybe not.
Branches might be slow, but maybe not a few extra calls worth. It
makes no sense to try to optimise portable code beyond the obvious
things like taking unneeded calculations out of loops.

We can write for simplicity, to make the whole thing simpler which
reduces debugging effort and maybe decreases maintenance costs, and
the simpler code may be easier to find algorithmic improvements etc.
But simplicity is an esthetic judgement. What looks simple to one
person may look complex to another. My preference is to make it look
simpler to *me* since if the code ever does get maintained by someone
else I can't predict who'll do it. So I might as well get the benefits
now.

Writing to be easy for other people to read, it's better to avoid
subtle ideas. Anything that people find hard to understand before the
first cup of coffee is a problem.

This time around I'd say that all the 1-minute solutions are good, and
the things I put more time into come out worse.

Bruce McFarling

unread,
Mar 3, 2007, 12:17:08 PM3/3/07
to
On Mar 3, 11:09 am, "J Thomas" <jethom...@gmail.com> wrote:
> Writing to be easy for other people to read, it's better to avoid
> subtle ideas. Anything that people find hard to understand before the
> first cup of coffee is a problem.

That's why I liked John Passiniti's factoring ... it was easy enough
for me to read to directly add the pretty printing into the main loop
when I didn't like the way it printed ... it only took two iterations
to get it exactly right, which would be a record for me ... and then
that could come directly out as a word rather than being handled in
multiple locations.

The test of whether something is easy for other people to read
is other people trying to add a capability.

Gerry

unread,
Mar 3, 2007, 1:16:10 PM3/3/07
to

I'm not disagreeing with you but would just point out that it's
trivial to add pretty printing to other versions.

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 2 .r then


3vec over and if ." fizz" then
5vec over and if ." buzz" then

dup 1 = if cr else space then
1 rshift
loop
drop
;

Frank Buss

unread,
Mar 3, 2007, 3:30:50 PM3/3/07
to
On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
> wrote:
>> A simple solution is
>>
>>: bang
>> 100 1 do
>> i 15 mod 0= if ." FizzBuzz " else
>> i 3 mod 0= if ." Fizz " else
>> i 5 mod 0= if ." Buzz " else
>> i . then then then
>> loop ;

You wouldn't get the job, because you missed 100.

My first try was this:

: fizzbuzz
101 1 do
i 3 mod 0 = dup if ." Fizz" then
i 5 mod 0 = dup if ." Buzz" then
invert swap invert and if i . then
cr
loop ;

A bit like John's solution (is ENDIF ANS Forth?), but I have still to learn
more Forth.

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

Bruce McFarling

unread,
Mar 3, 2007, 5:29:22 PM3/3/07
to
On Mar 3, 1:16 pm, "Gerry" <g...@jackson9000.fsnet.co.uk> wrote:
> I'm not disagreeing with you but would just point out that it's
> trivial to add pretty printing to other versions.

The fact that you can add it to yours factored out as a single
word (I'll take your word for it that it factors out) is nowhere
near the same stress test as the fact that I can add it factored
out as a single word to John's.

But, yes, right aligned is even prettier

: fizz? ( n -- fl ) 3 MOD 0= DUP IF ." Fizz" THEN ;
: buzz? ( n -- fl ) 5 MOD 0= DUP IF ." Buzz" THEN ;

: ?.number ( n fl1 fl2 -- fl1 fl2 )
2DUP 2>R OR 0= IF 2 .R THEN 2R> ;

: .FizzBuzz-WS ( fl1 fl2 -- ) AND IF CR ELSE SPACE THEN ;

: .FizzBuzz-item ( n -- ) DUP fizz? OVER buzz? ?.number .FizzBuzz-WS ;

: bang1 ( -- ) 101 1 DO I .FizzBuzz-item LOOP ;

CR CR .( bang1 ) CR bang1 CR CR

Doug Hoffman

unread,
Mar 3, 2007, 6:13:45 PM3/3/07
to
On Feb 28, 7:42 pm, John Passaniti <n...@JapanIsShinto.com> wrote:

My first solution (i.e. 1-minute solution) was this:

: fizz? ( n -- flag )

3 mod 0= ;

: buzz? ( n -- flag )

5 mod 0= ;

0 value notfound

: FizzBuzz
101 1 DO cr true to notfound
i fizz? IF ." Fizz" false to notfound THEN
i buzz? IF ." Buzz" false to notfound THEN
notfound IF i . THEN
LOOP ;

Interesting that we both chose exactly the same names to factor out.

I knew that a local variable would be better than a global, but I was
thinking in terms of something that might be viewed by many non-Forth
programmers. But I also had a nagging feeling that something was not
quite right. When I saw your solution it answered that question as
you had the better, no variable at all, solution. Nice.

-Doug

Andrew Haley

unread,
Mar 4, 2007, 6:53:17 AM3/4/07
to
Frank Buss <f...@frank-buss.de> wrote:
> On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
>> wrote:
>>> A simple solution is
>>>
>>>: bang
>>> 100 1 do
>>> i 15 mod 0= if ." FizzBuzz " else
>>> i 3 mod 0= if ." Fizz " else
>>> i 5 mod 0= if ." Buzz " else
>>> i . then then then
>>> loop ;

> You wouldn't get the job, because you missed 100.

When you removed my "I'm assuming a half-open interval" comment, were
you being deliberately deceitful?

Andrew.

J Thomas

unread,
Mar 5, 2007, 12:41:24 PM3/5/07
to
On Mar 3, 9:37 am, "Gerry" <g...@jackson9000.fsnet.co.uk> wrote:

> Nothing wrong with other solutions but a simple solution avoiding
> nested ifs etc uses bit vectors. The constants can be stuck in line
>
> 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
> ;

....

> As the perpetrator of this solution I'm very interested to know if it
> is generally considered good or bad, be as rude as you like I'm not
> the suicidal type. I'm rather surprised that you found it hard to read
> - I suppose a 2 line comment describing the method would have helped.

Yes, I think a 2 line comment would have done wonders.

I looked at it and saw some magic numbers. What are they for? Then I
looked at what you were doing with them, and it appeared you were
taking the I value and doing OVER AND 0= and getting correct results.
How could you do that? Did you have some advanced mathematical method
I'd never heard of? I tried to imagine how it could work and had no
idea. So before I did anything else I copied the code to a Forth and
tried it and verified that yes indeed, it did work.

Then I held my breath and decided that no matter how arcane it was, I
could understand it, all I had to do was study it. I looked closer at
the magic numbers. When I switched from hex to binary in my head I
noticed that 3vec had every third bit set. Ah! And then 5vec had every
fifth bit set. Then it was obvious except for the details. probe was
set to #4000, it needed to be reset precisely when both cycles were
complete, so bit 14 was the first digit that would work, do it exactly
15 times and reset. If the numbers had been 5 and 7 it would have been
harder to do.

There's nothing wrong with this. On a system where division is slow it
will run fast. On a system where nesting is slow and division is fast,
it will run slow. It isn't a method that's obvious with no comments
before the first cup of coffee. I was slow to see it partly because I
was ready to believe it might be something arcane I'd never seen
before at all, that I might have trouble with.

If we're going to judge readability, compare it with Andrew Haley's
solution.

: bang
100 1 do
i 15 mod 0= if ." FizzBuzz " else
i 3 mod 0= if ." Fizz " else
i 5 mod 0= if ." Buzz " else
i . then then then
loop ;

This solution has absolutely nothing unexpected. First case. Second
case. Third case. Fourth case. Precisely one of the four paths will be
taken. No trickiness about executing the Buzz path after the Fizz path
has already gone through. Nothing tricky anywhere. The code is bigger
than other good solutions. It will take longer to run than some --
more than half the time you do 3 mods and 3 elses. But there's nothing
here that can be misunderstood.

Readability isn't the only issue. Speaking for myself, I like to see
elegant solutions and new ideas. But the most-readable code won't have
anything new and it won't have anything that's shorter than
unexpected. Ideally it will appear to do exactly what the specs call
for, one requirement matching up with one stretch of code. So you go
down the list and see yes it did this, yes it did that, until you get
to the end. The only way Andrew's code misses this (apart from the
idea shared by everybody except computer programmers that when you say
"one to a hundred" it means start from 1 and continue until you've
done 100) is that the divisible-by-both case comes out of order.

Andrew Haley

unread,
Mar 5, 2007, 1:15:33 PM3/5/07
to
J Thomas <jeth...@gmail.com> wrote:

> If we're going to judge readability, compare it with Andrew Haley's
> solution.

> : bang
> 100 1 do
> i 15 mod 0= if ." FizzBuzz " else
> i 3 mod 0= if ." Fizz " else
> i 5 mod 0= if ." Buzz " else
> i . then then then
> loop ;

> This solution has absolutely nothing unexpected. First case. Second
> case. Third case. Fourth case. Precisely one of the four paths will be
> taken. No trickiness about executing the Buzz path after the Fizz path
> has already gone through. Nothing tricky anywhere. The code is bigger
> than other good solutions.

No it isn't. AFAIK it's the smallest, both in the size of the source
and the object. John Passaniti's gets close, but it's 584 bytes long
whereas mine was 576. [AMD64 gforth.] Not that I was trying for small
size -- that was just a side-effect of a simple solution.

Andrew.

J Thomas

unread,
Mar 5, 2007, 2:12:20 PM3/5/07
to
On Mar 5, 1:15 pm, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:
> J Thomas <jethom...@gmail.com> wrote:

> > This solution has absolutely nothing unexpected. First case. Second
> > case. Third case. Fourth case. Precisely one of the four paths will be
> > taken. No trickiness about executing the Buzz path after the Fizz path
> > has already gone through. Nothing tricky anywhere. The code is bigger
> > than other good solutions.
>
> No it isn't. AFAIK it's the smallest, both in the size of the source
> and the object. John Passaniti's gets close, but it's 584 bytes long
> whereas mine was 576. [AMD64 gforth.] Not that I was trying for small
> size -- that was just a side-effect of a simple solution.

I'm surprised. It has redundant code, I'd expect it to be larger.

John's code has surplus definitions and surplus calls due to his
factoring. If the headers count that will push up his size, if you
only count code then it's just a couple of extra calls.

With gForth 0.6.2

unused : bang


100 1 do
i 15 mod 0= if ." FizzBuzz " else
i 3 mod 0= if ." Fizz " else
i 5 mod 0= if ." Buzz " else
i . then then then

loop ; unused - .

got 304.

unused : bang
101 1 do
cr
i 3 mod 0= dup if ." Fizz" then
i 5 mod 0= dup if ." Buzz" then
or 0= if i . then
loop ; unused - .

gave 228.

: bang
101 1 do
cr
i 5 mod 0=
i 3 mod 0=
2dup or 0= if i . then
if ." Fizz" then
if ." Buzz" then
loop ;

gave 224 because it uses 2DUP in place of DUP DUP . It's harder to
read.

Andrew Haley

unread,
Mar 5, 2007, 2:32:50 PM3/5/07
to
J Thomas <jeth...@gmail.com> wrote:
> On Mar 5, 1:15 pm, Andrew Haley <andre...@littlepinkcloud.invalid>
> wrote:
>> J Thomas <jethom...@gmail.com> wrote:

>> > This solution has absolutely nothing unexpected. First case. Second
>> > case. Third case. Fourth case. Precisely one of the four paths will be
>> > taken. No trickiness about executing the Buzz path after the Fizz path
>> > has already gone through. Nothing tricky anywhere. The code is bigger
>> > than other good solutions.
>>
>> No it isn't. AFAIK it's the smallest, both in the size of the source
>> and the object. John Passaniti's gets close, but it's 584 bytes long
>> whereas mine was 576. [AMD64 gforth.] Not that I was trying for small
>> size -- that was just a side-effect of a simple solution.

> I'm surprised. It has redundant code, I'd expect it to be larger.

> John's code has surplus definitions and surplus calls due to his
> factoring. If the headers count

Well, yeah. Of course they count! I mean, someone had to pay for
that RAM they're in...

> that will push up his size, if you only count code then it's just a
> couple of extra calls.

Andrew.

John Passaniti

unread,
Mar 6, 2007, 1:46:03 AM3/6/07
to
J Thomas wrote:
> I'm surprised. It has redundant code, I'd expect it to be larger.

The point of FizzBuzz is that it is part of a coding skill test given to
job candidates. The author's claim is that many job candidates can't
code, and tests like FizzBuzz are used to weed such weaker candidates out.

My response in comp.lang.forth was simply to provide the original poster
an example of what he wanted-- how would the solution to FizzBuzz look
in Forth. One assumes that the original poster understood that there
are endless variations on a theme. One assumes the original poster
understands that one can optimize for different things-- size, speed,
clarity, programmer effort, generality, reusability, etc.

> John's code has surplus definitions and surplus calls due to his
> factoring. If the headers count that will push up his size, if you
> only count code then it's just a couple of extra calls.

If the problem statement for FizzBuzz had said, "minimize code size" or
"generalize your code so that other multiples can print out different
messages" then the choices I made would have been different. This is
why context matters. In a job interview, I would have taken less than a
minute to code my solution and would have been able to move on to other
problems. Those of you who are agonizing over if an additional term
could be factored out or are counting the number of bytes generated are
the ones who might have less time for the more substantive questions
during the interview.

Gerry

unread,
Mar 6, 2007, 6:57:30 AM3/6/07
to

Sorry I caused you some grief with the code. I must make an effort
with making my code more readable, after all its far less effort for 1
writer to do that compared to n readers decoding something.

Gerry

Doug Hoffman

unread,
Mar 6, 2007, 7:59:49 AM3/6/07
to
On Mar 6, 1:46 am, John Passaniti <put-my-first-name-

Your solution, in addition to excellent clarity and simplicity, IMO,
is also easily extensible. Consider the case where the problem is
expanded to include Boom (divisible by 7) and Zapp (divisible by 9):

: boom? ( n -- flag ) \ 0 if hit
7 mod 0= dup IF ." Boom" THEN ;

: zapp? ( n -- flag ) \ 0 if hit
9 mod 0= dup IF ." Zapp" THEN ;

: FizzBuzzBoomZapp
101 1 DO cr


i fizz?
i buzz? or

i boom? or
i zapp? or
0= IF i . THEN
LOOP ;

This is a reasonable variation of the original problem.
So 42 would yield FizzBoom and 45 would yield FizzBuzzZapp and so
forth. Two of your original definitions were re-usable.

-Doug

J Thomas

unread,
Mar 6, 2007, 8:32:01 AM3/6/07
to
On Mar 6, 1:46 am, John Passaniti <put-my-first-name-
h...@JapanIsShinto.com> wrote:
> J Thomas wrote:
> > I'm surprised. It has redundant code, I'd expect it to be larger.
>
> The point of FizzBuzz is that it is part of a coding skill test given to
> job candidates. The author's claim is that many job candidates can't
> code, and tests like FizzBuzz are used to weed such weaker candidates out.
>
> My response in comp.lang.forth was simply to provide the original poster
> an example of what he wanted-- how would the solution to FizzBuzz look
> in Forth. One assumes that the original poster understood that there
> are endless variations on a theme. One assumes the original poster
> understands that one can optimize for different things-- size, speed,
> clarity, programmer effort, generality, reusability, etc.
>
> > John's code has surplus definitions and surplus calls due to his
> > factoring. If the headers count that will push up his size, if you
> > only count code then it's just a couple of extra calls.
>
> If the problem statement for FizzBuzz had said, "minimize code size" or
> "generalize your code so that other multiples can print out different
> messages" then the choices I made would have been different.

I didn't at all intend to criticise your code. I think it is
excellent. The factoring is not a bad thing -- though I think the code
is a little more readable without it, and the factored definitions
that are used only once increase the code size a little. That isn't
any big deal, and in the context you were responding to -- a job
interview for a Forth job -- would show that you have the factoring
concept firmly in mind.

The lesson I got from this is that factoring into small short pieces
can impede readability, and that careful naming can reduce that
problem.

The lesson I got from my own experiments was that anything tricky
reduces readability and should be used only when there are clear
advantages that override that disadvantage.

Again, I meant no criticism. I thought that every solution except mine
was excellent. Gerry's solution had the disadvantage of not being what
a beginning Forth programmer would expect, which made it harder to
read. But in some systems his tests would be far faster. Of course,
more than half the time you're doing up to 3 mods or dmods to generate
a numeric string to . so it won't be that big a percentage
improvement.

Once again, as you point out, there was nothing about minimising code
size in the specs and there's no shame in not having the smallest code
when compiled on some particular system. I have no complaint about
your 1-minute code. I don't think it should be the standard for
judging code size since it's large, and that says nothing bad about
you or your code.

J Thomas

unread,
Mar 6, 2007, 8:37:35 AM3/6/07
to
On Mar 6, 6:57 am, "Gerry" <g...@jackson9000.fsnet.co.uk> wrote:

> Sorry I caused you some grief with the code. I must make an effort
> with making my code more readable, after all its far less effort for 1
> writer to do that compared to n readers decoding something.

It's no big deal. I wanted to note that code is more readable when it
does nothing unexpected. Your innovative solution was fundamentally
harder to read because it was innovative. Writing carefully to expose
your methods, along with careful documentation, could palliate that.
But anything that your readers don't expect is harder to read than
what they do expect, and there's no getting around that.

The Beez'

unread,
Mar 6, 2007, 10:27:38 AM3/6/07
to
This is a slight variation on that one (4tH only):

: zap dup 0 .r ; : fizz ." Fizz" ; : buzz ." Buzz" ; : fizzbuzz fizz
buzz ;
create foo ' zap , ' fizz , ' buzz , ' fizzbuzz ,
:this foo does> >r dup 5 mod 0= 2* over 3 mod 0= + cells r> + @c
execute drop ;
: bar 101 1 do i foo space loop ; bar

Hans Bezemer

Bruce McFarling

unread,
Mar 6, 2007, 10:34:22 AM3/6/07
to
On Mar 6, 6:57 am, "Gerry" <g...@jackson9000.fsnet.co.uk> wrote:
> Sorry I caused you some grief with the code. I must make
> an effort with making my code more readable, after all
> its far less effort for 1 writer to do that compared to
> n readers decoding something.

Ah, but a benefit of open-source is that the effort by
the one writer does not have to always be the code author.

Applying the rule, "explain magic numbers", I reckon
it gets to between first and second cup of coffee
stage for me, which for reasonably handy programmers
would probably push it back before the first cup.

hex

1249 constant 3vec \ = 0001001001001001, each 3rd bit set
0421 constant 5vec \ = 0000010000100001, each 5th bit set
4000 constant probe \ = 0100000000000000, edge of frame

Jerry Avins

unread,
Mar 6, 2007, 1:33:08 PM3/6/07
to

I prefer

binary

0001001001001001 constant 3vec \ each 3rd bit set
0000010000100001 constant 5vec \ 5th bit set
0100000000000000 constant probe \ edge of frame


3vec 5vec or constant 3or5vec

decimal

When a particular BASE is native to the problem, use it.

Jerry
--
Engineering is the art of making what you want from things you can get.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

Bruce McFarling

unread,
Mar 6, 2007, 3:32:05 PM3/6/07
to
On Mar 6, 1:33 pm, Jerry Avins <j...@ieee.org> wrote:
> > hex
> > 1249 constant 3vec \ = 0001001001001001, each 3rd bit set
> > 0421 constant 5vec \ = 0000010000100001, each 5th bit set
> > 4000 constant probe \ = 0100000000000000, edge of frame
> > 3vec 5vec or constant 3or5vec
> > decimal

> I prefer

> binary
> 0001001001001001 constant 3vec \ each 3rd bit set
> 0000010000100001 constant 5vec \ 5th bit set
> 0100000000000000 constant probe \ edge of frame
> 3vec 5vec or constant 3or5vec
> decimal

Meaning that the commented above is better than the
uncommented, and the base that conveys the information
directly is even better?

I have no problem with that ranking at all.

numbers conv

ata...@gismu.com

unread,
Mar 7, 2007, 1:12:59 AM3/7/07
to
In pygmy forth:

: n DUP . 1+ ;
: f ." Fizz " 1+ ;
: b ." Buzz " 1+ ;
: fb ." FizzBuzz " 1+ ;
: y n n f n b f n n f b ;
: x y n f n n fb ;
: go 1 x x x x x x y DROP ;

Doug Hoffman

unread,
Mar 7, 2007, 7:35:23 AM3/7/07
to

Well, sometimes brute force will work. But if the problem description
changes then that approach often isn't helpful. For example, try
that for solving the FizzBuzzBoomZapp problem for say 1 through 1000.

-Doug

Bruce McFarling

unread,
Mar 7, 2007, 10:03:26 AM3/7/07
to
On Mar 7, 7:35 am, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
012345678901234567890123456789012345678901234567890123456789

> Well, sometimes brute force will work. But if the problem
> description changes then that approach often isn't
> helpful. For example, try that for solving the
> FizzBuzzBoomZapp problem for say 1 through 1000.

Ah, but you only have to find the LCD of the factors and
implement it for the LCD, with the numbers adding a value
passed to them on the stack and passing that value on
unmodified, then find the Modulus of the loop limit and
do a simple FOR loop, adding the LCD each time through
the loop, followed by enough of the LCD sequence to get
through to the loop terminus.

Of course, implementing the LCD and Modulus routines
are left as an exercise for the reader.

Doug Hoffman

unread,
Mar 7, 2007, 10:10:14 AM3/7/07
to

Or you could just use:

: FizzBuzzBoomZapp
1001 1 DO cr


i fizz?
i buzz? or
i boom? or
i zapp? or
0= IF i . THEN
LOOP ;

Q.E.D.

-Doug

J Thomas

unread,
Mar 7, 2007, 10:55:33 AM3/7/07
to

Which way is brute force? One way you do the calculations once and
never again. The other way you repeat them every time.

However, if the critical values are 3 5 7 11 then the least common
multiple is 1155 . You aren't going to have a lot of regular repeating
patterns in that. Better to use bit vectors or simulated bit vectors
than try to code it all into the definition.

I find your way far more readable. Anything innovative is harder to
read because the reader has to think.

Doug Hoffman

unread,
Mar 7, 2007, 12:06:19 PM3/7/07
to

It also took less than 30 seconds to code and worked first time
through.

Anything innovative is harder to
> read because the reader has to think.

What is the goal? Solving a problem or being "innovative"? One could
argue that John P's approach is indeed innovative, if that is deemed
important here.

-Doug

Bruce McFarling

unread,
Mar 7, 2007, 12:28:18 PM3/7/07
to
On Mar 7, 12:06 pm, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
> > > : FizzBuzzBoomZapp
> > > 1001 1 DO cr
> > > i fizz?
> > > i buzz? or
> > > i boom? or
> > > i zapp? or
> > > 0= IF i . THEN
> > > LOOP ;

> What is the goal? Solving a problem or being "innovative"?


> One could argue that John P's approach is indeed innovative,
> if that is deemed important here.

I prefer:

: bang3 ( -- ) 1001 1 DO i .FizzBuzzBoomZapp LOOP ;

John's approach translates directly to

: .FizzBuzzBoomZapp ( n -- )
DUP >R fizz?
R@ buzz? or
R@ boom? or
R@ zapp? or


0= IF i . THEN

RDROP ;

: bang3 ( -- ) 1001 1 DO i .FizzBuzzBoomZapp LOOP ;

Of course, it only makes a difference when it is
a real problem with a real world side-effect
as the primary target, and it comes time to
determine why the loop is not working correctly.
At that point, you'll want to do that factoring
between the loop and the loop action if you haven't
done so already.

As far as readability, if you are in the habit
of reading from back to front, the longer the
individual definition the more annoying.

Clever Monkey

unread,
Mar 7, 2007, 2:06:49 PM3/7/07
to

We have our Obfuscated Forth Contest winner.

Ian Osgood

unread,
Mar 7, 2007, 3:03:28 PM3/7/07
to
On Mar 6, 4:59 am, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:

> Your solution, in addition to excellent clarity and simplicity, IMO,
> is also easily extensible. Consider the case where the problem is
> expanded to include Boom (divisible by 7) and Zapp (divisible by 9):
>
> : boom? ( n -- flag ) \ 0 if hit
> 7 mod 0= dup IF ." Boom" THEN ;
>
> : zapp? ( n -- flag ) \ 0 if hit
> 9 mod 0= dup IF ." Zapp" THEN ;
>
> : FizzBuzzBoomZapp
> 101 1 DO cr
> i fizz?
> i buzz? or
> i boom? or
> i zapp? or
> 0= IF i . THEN
> LOOP ;
>
> This is a reasonable variation of the original problem.
> So 42 would yield FizzBoom and 45 would yield FizzBuzzZapp and so
> forth. Two of your original definitions were re-usable.
>
> -Doug

Exercise: use CREATE-DOES> to remove the duplication between the *?
words.

Ian

Chuck Adams

unread,
Mar 7, 2007, 3:09:42 PM3/7/07
to
I believe my answer to the problem would go something like this:

: fizzbuzzoff ." I don't care for gimmicks. Good day" ;

When I interview a candidate, I prefer to pose real challenges that
touch on experience. No one interviewing an engineer asks them to
build a birdhouse.


John Passaniti

unread,
Mar 7, 2007, 6:44:49 PM3/7/07
to
Chuck Adams wrote:
> I believe my answer to the problem would go something like this:
>
> : fizzbuzzoff ." I don't care for gimmicks. Good day" ;
>
> When I interview a candidate, I prefer to pose real challenges that
> touch on experience.

I've worked with engineers who were terrible programmers. I've also
worked with engineers who simply didn't have a lot of experience
programming. Being a great engineer says nothing about your ability to
express the solution to a problem in code. The point of FizzBuzz-style
questions is to weed these candidates out (or to better identify their
strengths if they are hired).

FizzBuzz-style questions are also great if they have an element of
ambiguity to them. This forces the candidate to not just dive into the
problem, but to interact with you and resolve any questions and clarify
requirements. And quite often I've found, you learn the most about a
candidate not by the actual work on solving the FizzBuzz-style question,
but prior to it-- during the part when there is a back-and-forth in
better defining the problem. Are they asking intelligent questions?
Are they over-analyzing the problem? Are they asking questions because
they suffer from "specification paralysis."

> No one interviewing an engineer asks them to
> build a birdhouse.

Your refusal to do a FizzBuzz-style question because it doesn't pose a
"real challenge" would itself tell me something about your character and
likely experience. It might also say something about your respect for
others.

In terms of character, it might indicate you are a prima donna engineer
who likely wouldn't fit in with our company's culture. We don't believe
that any of us are so special that we are "above" any kind of work.

In terms of experience, your refusal to do a FizzBuzz-style question
might indicate a lack of real-world experience. Because in the real
world, although it sure would be nice if you could always be presented
with the most challenging work possible, sometimes you have to get your
hands dirty and just sling out some low-level code to solve an immediate
problem.

In terms of respect, you are applying for a job and you likely aren't
the only one. Most companies (like the one I work for) don't have
infinite resources, so any new hires represent a serious investment by
the company in you. The company conducts a job interview not to amuse
you for an afternoon, but to tell if an investment in you is worthwhile.
So we will ask you FizzBuzz-style questions as well as higher-level
questions to determine if you're the best candidate for the job.

Don't want to take the test? That's nice. Don't let the door slam you
on the ass on your way out.

Doug Hoffman

unread,
Mar 7, 2007, 7:51:30 PM3/7/07
to
On Mar 7, 12:28 pm, "Bruce McFarling" <agil...@netscape.net> wrote:

> John's approach translates directly to
>
> : .FizzBuzzBoomZapp ( n -- )
> DUP >R fizz?
> R@ buzz? or
> R@ boom? or
> R@ zapp? or
> 0= IF i . THEN
> RDROP ;
>
> : bang3 ( -- ) 1001 1 DO i .FizzBuzzBoomZapp LOOP ;
>
> Of course, it only makes a difference when it is
> a real problem with a real world side-effect
> as the primary target, and it comes time to
> determine why the loop is not working correctly.
> At that point, you'll want to do that factoring
> between the loop and the loop action if you haven't
> done so already.

Yes. I agree that is a reasonable factoring. As you say, it allows
for easy testing of the inner engine, divorced from the loop.

-Doug

Ron Aaron

unread,
Mar 7, 2007, 8:06:56 PM3/7/07
to
On Feb 28, 3:16 pm, keo...@gmail.com wrote:

Here's a solution (not the simplest) using Reva (note there are
distinctly non ANS words here):

create fizzbuzz " FizzBuzz" here,
variable fizzed

: fizzbuzz? ( i a mod -- i )
2 pick swap mod
0if 4 type fizzed on ;then drop ;

: num? ( i -- ) fizzed @ if space drop ;then . ;
: fizz? ( i -- i ) fizzbuzz 3 fizzbuzz? ;
: buzz? ( i -- i ) fizzbuzz cell+ 5 fizzbuzz? ;

: fb 101 1 do fizzed off i fizz? buzz? num? loop ;


I tried to avoid using conditionals and preferred to factor pretty
heavily. It depends on 4 byte cells, which Reva uses. It also adds a
space after the output word, for cleaner looking output.

Bruce McFarling

unread,
Mar 7, 2007, 9:58:10 PM3/7/07
to
On Mar 7, 12:28 pm, "Bruce McFarling" <agil...@netscape.net> wrote:
> John's approach translates directly to
>
> : .FizzBuzzBoomZapp ( n -- )
> DUP >R fizz?
> R@ buzz? or
> R@ boom? or
> R@ zapp? or
> 0= IF i . THEN
> RDROP ;

> : bang3 ( -- ) 1001 1 DO i .FizzBuzzBoomZapp LOOP ;

That'll teach me to send code from a computer without
a Forth installed:

: .FizzBuzzBoomZapp ( n -- )
DUP >R fizz?
R@ buzz? or
R@ boom? or
R@ zapp? or

0= IF R@ . THEN
RDROP ;

The last, conditional, I is, after all, which its
an RDROP at the end instead of a final R> ... and
then I didn't replace it.

Bernd Paysan

unread,
Mar 8, 2007, 4:57:06 AM3/8/07
to
Clever Monkey wrote:

Is it obfuscated? I think it's the ColorForth entry, because it uses the
most simple basics (apart from ." and .), and doesn't loop.

--
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://www.jwdt.com/~paysan/

Andrew Haley

unread,
Mar 8, 2007, 5:14:18 AM3/8/07
to
Chuck Adams <cja...@gmail.com> wrote:
> I believe my answer to the problem would go something like this:

> : fizzbuzzoff ." I don't care for gimmicks. Good day" ;

> When I interview a candidate, I prefer to pose real challenges that
> touch on experience.

Sure, but these microproblems can be very revealing. Some apparently
fantastically knowledgeable people can fail, and that tells you
something. Also, having written code in an interview such a
microproblem, I was once asked what code the compiler I used would
generate for it. That, I tell you, really separates the sheep from
the goats.

Andrew.

Clever Monkey

unread,
Mar 8, 2007, 2:19:34 PM3/8/07
to
Bernd Paysan wrote:
> Clever Monkey wrote:
>
>> ata...@gismu.com wrote:
>>> In pygmy forth:
>>>
>>> : n DUP . 1+ ;
>>> : f ." Fizz " 1+ ;
>>> : b ." Buzz " 1+ ;
>>> : fb ." FizzBuzz " 1+ ;
>>> : y n n f n b f n n f b ;
>>> : x y n f n n fb ;
>>> : go 1 x x x x x x y DROP ;
>>>
>> We have our Obfuscated Forth Contest winner.
>
> Is it obfuscated? I think it's the ColorForth entry, because it uses the
> most simple basics (apart from ." and .), and doesn't loop.
>
It's obfuscated to my eyes, but I don't think in Forth anymore.

Doug Hoffman

unread,
Mar 9, 2007, 5:50:40 PM3/9/07
to

Once again I see words. But no results. Where is the better
definition?

Chuck Adams

unread,
Mar 9, 2007, 6:28:43 PM3/9/07
to
On Mar 7, 3:44 pm, John Passaniti <n...@JapanIsShinto.com> wrote:
[... cut to the summary, we all have threaded readers ...]

> Don't want to take the test? That's nice. Don't let the door slam you
> on the ass on your way out.

I answer technical questions just fine, and I ask them as well when
interviewing candidates. But of course, I always run into people
online who talk about who they'd "fire on the spot", or who they
wouldn't consider hiring based on some narrow technical case, and I'm
always curious as to whether they've ever actually been in that
position, or if it's just another case of "If I Ran the Zoo", or the
phenomenom where everyone on the internet is an amateur lawyer.

Consider for a moment that my answer was hyperbole. Consider what an
awesome waste of time it was to dress me down in such voluminous
prose. Maybe it's just that this microproblem was just so durned
silly... but I still stand by my aspersion.

chuck

J Thomas

unread,
Mar 9, 2007, 6:34:39 PM3/9/07
to
On Mar 9, 5:50 pm, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
> On Mar 7, 10:55 am, "J Thomas" <jethom...@gmail.com> wrote:
> > On Mar 7, 10:10 am, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
>
> > > Well, sometimes brute force will work. But if the problem description
> > > changes then that approach often isn't helpful. For example, try
> > > that for solving the FizzBuzzBoomZapp problem for say 1 through 1000.

> > Which way is brute force? One way you do the calculations once and


> > never again. The other way you repeat them every time.
>
> > However, if the critical values are 3 5 7 11 then the least common
> > multiple is 1155 . You aren't going to have a lot of regular repeating
> > patterns in that. Better to use bit vectors or simulated bit vectors
> > than try to code it all into the definition.
>
> Once again I see words. But no results. Where is the better
> definition?

It depends on your needs which is better.

The way you implied was brute force did an absolute minimum of
calculation at runtime. It had the loop unrolled, but factored. very
elegant, though you had to understand what it was doing to read the
code.

The other extreme did three divisions every time. It seemed to come
natural to a lot of people.

The intermediate did three table lookups every time, and it had things
arranged so the tables were bit-vectors and were easy to access. No
divisions, ever, except for those inherent in printing out numbers.
(Do it in hex and you can avoid those too.) Slower than the loop-
unrolling method, simpler than the brute force method, but again you
have to understand how it works to read the code.

I pointed out that the advantages of loop-unrolling for this problem
evaporate when the problem changes with more prime numbers and higher
N. Cycle length 15 is easy. Cycle length 1155 is not so easy. You
could find shorter cycles that do get repeated and alternate them, and
the code will be even less readable.

But the table lookups work fine then. You can use 4 tables for 4
variables, and OR the results.

> Once again I see words. But no results. Where is the better
> definition?

Was I unclear? Which is best depends on your goals. The loop-unrolling
method is the fastest. The bit-vector method is the most elegant. The
brute force method is the most readable.

Doug Hoffman

unread,
Mar 9, 2007, 7:15:26 PM3/9/07
to
On Mar 9, 6:34 pm, "J Thomas" <jethom...@gmail.com> wrote:

[big snip]

> Was I unclear?

Yes.

> Which is best depends on your goals. The loop-unrolling
> method is the fastest. The bit-vector method is the most elegant. The
> brute force method is the most readable.

Let's just solve the problem. My programming time is valuable. Last
I heard computers are pretty good at executing code. Where is the
better (elegant? maintainable? whatever?) solution?

J Thomas

unread,
Mar 9, 2007, 7:28:05 PM3/9/07
to

Now you are unclear. Tell us what "good" means to you and then you can
tell us what solution you like best.

Doug Hoffman

unread,
Mar 9, 2007, 7:53:07 PM3/9/07
to

Let's just solve the problem. Where is your solution (based on
elegance, maintainabillty, whatever, you pick)? I've already given my
solution.

J Thomas

unread,
Mar 9, 2007, 8:34:13 PM3/9/07
to

I like most of the solutions that have been presented. But if you want
the ultimate of maintainability, something like this is likely to be
good:

10 FOR I = 1 TO 100
20 IF (I MOD 3 = 0) THEN PRINT "Fizz";
30 IF (I MOD 5 = 0) THEN PRINT "Buzz";
40 IF ((I MOD 3 <> 0) AND (I MOD 5 <> 0)) THEN PRINT I;
50 PRINT
60 NEXT I

I don't really think that BASIC is easier to learn than Forth. But a
lot of people think it is, and a lot of people learn it very quickly
-- maybe partly because their confidence is so high. Practically
anybody can quickly learn enough BASIC to maintain this program.

It just doesn't get much more readable and maintainable than this.

Your time is valuable. The time of a high-school graduate who can
maintain a program like this is not. This program is the cheapest to
maintain.

Doug Hoffman

unread,
Mar 9, 2007, 8:49:26 PM3/9/07
to
On Mar 9, 8:34 pm, "J Thomas" <jethom...@gmail.com> wrote:


> I like most of the solutions that have been presented. But if you want
> the ultimate of maintainability, something like this is likely to be
> good:
>
> 10 FOR I = 1 TO 100
> 20 IF (I MOD 3 = 0) THEN PRINT "Fizz";
> 30 IF (I MOD 5 = 0) THEN PRINT "Buzz";
> 40 IF ((I MOD 3 <> 0) AND (I MOD 5 <> 0)) THEN PRINT I;
> 50 PRINT
> 60 NEXT I
>
> I don't really think that BASIC is easier to learn than Forth. But a
> lot of people think it is, and a lot of people learn it very quickly
> -- maybe partly because their confidence is so high. Practically
> anybody can quickly learn enough BASIC to maintain this program.

You didn't solve the problem. The problem was clearly defined as
FizzBuzzBoomZapp. From 1 to 1000. Read up a few posts if you didn't
catch that. (Hint: It might help to use Forth. I'm fairly certain
this is a Forth newsgroup.)

ata...@gismu.com

unread,
Mar 9, 2007, 10:20:30 PM3/9/07
to

(Hope this isn't a dupe. Waited a day and didn't see my reply...)

Yes, it is a color forth or machine forth inspired solution.
But there is nothing particularly machine or color forth
specific. It really is just basic forth. If the toy names for
the toy problem don't suit you, here is a version with long
names and very slightly refactored. I find it harder to read
than the original.

: show.number DUP . 1+ ;
: show.fizz ." Fizz " 1+ ;
: show.buzz ." Buzz " 1+ ;
: show.fizz.buzz ." FizzBuzz " 1+
;
: show.1.5 show.number show.number show.fizz
show.number show.buzz
;
: show.6.10 show.fizz show.number show.number
show.fizz show.buzz
;
: show.11.15 show.number show.fizz show.number
show.number show.fizz.buzz
;
: show.10 show.1.5 show.6.10 ;
: show.15 show.10 show 11.15 ;
: show.30 show.15 show.15 ;
: show.90 show.30 show.30 show.30 ;
: go 1 show.90 show.10 DROP ;


Now the following code is much more fun! I have tried to solve
the Fizz Buzz Boom Zap problem with a machine forth like forth:
no "-", "/", "*", "mod", or any kind of loop; but with multiple
exit points, recursion, and tail recursion elimination. Since
it has to produce output, I will assume the usual ., .", and
type. Sadly I don't have a colorForth or machine forth that I
can use to debug the code. Worse still, my forth foo isn't good
enough to teach my forth about the needed machine forth
behavior.

I will use the A register operations. They are primitives in
various machine forths, here are working def's for other forths.

variable A
: A@ A @ ;
: A! A ! ;
: @A A@ @ ;
: !A+ A@ ! A@ cell + A! ;

( Here are some challenge problems. Define these words so they
act like the machine forth versions. )

( "XIF" acts like "dup if" and "XTHEN" like "then" )
: XIF ; immediate
: XTHEN ; immediate

( Acts like ":" but needs to allow recursion. )
: X: ; immediate

( Something like ";" This is probably a tough exercise.
Remember, it should translate tail calls to jumps -- tail
recursion elimination at the least. The easy way out is just a
jump to next. )
: X; ; immediate

( The pair X: ;X have to somehow manage compile vs. interpret.
Colored words might help. )

( --------- Now the real code --------- )

X: 1- -1 + X; X: 1+ 1 + X;

X: mod1+ ( mod n -- n+1 )
( only works for n such that 0 <= n < mod )
1+ swap over xor XIF drop X; XTHEN swap drop X;

X: p.name? ( f name mod -- f )
@A mod1+ dup !A+ ( f name new.residue ;
m[A] = new.residue )
XIF drop drop X; XTHEN ( f )
drop type drop 0 X; ( 0 )

X: Fizz? " Fizz" 3 p.name? X;
X: Buzz? " Buzz" 5 p.name? X;
X: Boom? " Boom" 7 p.name? X; ( or 9 )
X: Zap? " Zap" 11 p.name? X;
X: Number? ( f -- f ; m[A] incremented )
@A 1+ !A XIF @A . X; XTHEN ." " X;

( a: how exactly does color forth do this? b: how would you do
it without colors? The "2 2 +" is just to emphasize at the
problem. )
variable fbbz.residues 2 2 + allot

X: go1 ( -- )
fbbz.residues A!
1 Fizz? Buzz? Boom? Zap? Number? drop X;

X: go.loop go1 1- XIF go.loop X; XTHEN X;
X: go fbbz.residues A!
0 !A+ 0 !A+ 0 !A+ 0 !A+ 0 !A+
1000 go.loop X;

J Thomas

unread,
Mar 9, 2007, 10:41:47 PM3/9/07
to

Have I done something that offended you? I think I'm detecting a
certain coolness to your tone. I remember years when this newsgroup
was nice and mellow, experts and hobbyists and newbies got along real
well. And only a few years ago the only real irritants were Jeff Fox
and John Passaniti. Oh well.

I like your solution to the 4 variable problem. If there's any
improvement to be made it would be factoring your 4 subroutines, which
repeat the exact same code 4 times and change only the data.

Here's one way:

: makesub ( n -- ) ( E: i -- 0|n )
CREATE C, PARSE-NAME STRING,
DOES> TUCK C@ MOD TUCK 0= IF 1+ COUNT TYPE 0 THEN DROP ;

3 makesub fizz? Fizz
5 makesub buzz? Buzz
7 makesub boom? Boom
9 makesub zapp? Zapp

I find this code harder to read, but I'm guessing it might be slightly
more maintainable since instead of copying and pasting the definition
you could just do

11 makesub zilch? Zilch

etc.

On the other hand, if you're going to have a whole series of them, why
name them? You could put the addresses of the data structures into an
array, and loop through the array. But this is overkill.

I think this is somewhat less readable. But the code should be
smaller. We re-use the same code 4 times. When should we sacrifice
readability for size? On memory-limited systems, one of Forth's
traditional niches?


Now, the bit-array approach is very nice but it depends on 3 5 *
fitting into a cell, and 3 5 * 7 * does not fit except in a few
systems. We can do without the double, triple, and quadruple bit-
arrays. I'll use a similar method that I think is more readable.

in place of .... 3 MOD DUP 0= IF ." Fizz" THEN

we can use

.... 1 fizzle DUP 2@ = IF ! ." Fizz" FALSE EXIT THEN +! TRUE ....

We count to three, when we get there we reset the counter.

One advantage of this approach is that it can be maintained by
somebody who isn't real clear what MOD does. And it's faster when MOD
is slow compared to this long expression.

Anyway, I liked your solution. And if you want readability, nothing
can beat the BASIC version which I won't bother to modify for you.

Doug Hoffman

unread,
Mar 10, 2007, 5:37:01 AM3/10/07
to
On Mar 9, 10:41 pm, "J Thomas" <jethom...@gmail.com> wrote:
> On Mar 9, 8:49 pm, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
>
> > On Mar 9, 8:34 pm, "J Thomas" <jethom...@gmail.com> wrote:
>
> > > I like most of the solutions that have been presented. But if you want
> > > the ultimate of maintainability, something like this is likely to be
> > > good:
>
> > > 10 FOR I = 1 TO 100
> > > 20 IF (I MOD 3 = 0) THEN PRINT "Fizz";
> > > 30 IF (I MOD 5 = 0) THEN PRINT "Buzz";
> > > 40 IF ((I MOD 3 <> 0) AND (I MOD 5 <> 0)) THEN PRINT I;
> > > 50 PRINT
> > > 60 NEXT I
>
> > > I don't really think that BASIC is easier to learn than Forth. But a
> > > lot of people think it is, and a lot of people learn it very quickly
> > > -- maybe partly because their confidence is so high. Practically
> > > anybody can quickly learn enough BASIC to maintain this program.
>
> > You didn't solve the problem. The problem was clearly defined as
> > FizzBuzzBoomZapp. From 1 to 1000. Read up a few posts if you didn't
> > catch that. (Hint: It might help to use Forth. I'm fairly certain
> > this is a Forth newsgroup.)
>
> Have I done something that offended you? I think I'm detecting a
> certain coolness to your tone.

Earlier you wrote:

> Which way is brute force? One way you do the
> calculations once and never again. The other
> way you repeat them every time.

> However, if the critical values are 3 5 7 11
> then the least common multiple is 1155 .
> You aren't going to have a lot of regular repeating
> patterns in that. Better to use bit vectors or
> simulated bit vectors than try to code it all into
> the definition.

I wanted to see this "better" definition. Its existence was as I
suspected.


J Thomas

unread,
Mar 10, 2007, 7:11:37 AM3/10/07
to
On Mar 10, 5:37 am, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:

> > Have I done something that offended you? I think I'm detecting a
> > certain coolness to your tone.

> Earlier you wrote:

> > Which way is brute force? One way you do the
> > calculations once and never again. The other
> > way you repeat them every time.
> > However, if the critical values are 3 5 7 11
> > then the least common multiple is 1155 .
> > You aren't going to have a lot of regular repeating
> > patterns in that. Better to use bit vectors or
> > simulated bit vectors than try to code it all into
> > the definition.
>
> I wanted to see this "better" definition. Its existence was as I
> suspected.

?? Did it sound like I was saying the simulated bit vectors would be
better than your solution?

I intended to say that the loop-unrolling method would be far worse on
the larger problem, and the simulated bit-vectors would be better than
that.

For the simple problem the loop unrolling method depended on the
solution cycling every 15 times. So they could code the 15-cycle, and
then repeat that 6 times,and then do the first 10 again to get to 100.
Getting to 1000 scales easily, do the cycle 66 times and then the
first ten. But with 3 5 7 9 the cycle is 315 and unrolling the loop
by hand takes a lot of thinking. I guess you could find repetitions
within the 315 length and factor those.

So as the problem gets more complex the brute force method gets
relatively easier. Just tell the computer to work out all the details
for itself, every time.

Ah! By "code it all into the definition" I mean "unroll the loop so
you make no decisions at runtime". If it sounded like I was talking
about your solution, I apologise. I like your solution.

hel...@gmail.com

unread,
Mar 10, 2007, 8:22:31 AM3/10/07
to
On 8 Mrz., 01:06, "Ron Aaron" <rambam...@gmail.com> wrote:

Hi Ron,

> It depends on 4 byte cells, which Reva uses.

get that fixed if you ever want a 64bit version ;)

I'll introduce to other "alien" syntax solutions for 4p/HelFORTH
(tested with 4p, but they should work without much modification in
HelFORTH):

---------------------
: y vector dup . ;
: x"` ."` then> `space is y ;
: g <\ \\ \\ x" Fizz" \>
<\ \\ \\ \\ \\ x" Buzz" \>
y undo y 1+ ;
1 100 times g drop
---------------------

This first version uses "coroutines" as introduced with HelFORTH. I'll
explain it step by step.

1) The definition of "y" uses the RetroForth inspired kind of defered
words that do have a default behaviour. "vector" means simply to
compile a branch that by default goes to the next instruction. "is"
can change this to whatever target you want and "undo" resets this
jump to next instruction.

2) The definition of x" uses features borrowed by FreeForth. The `
(backtick) behind
: x"`
means that x" will be an immediate word. The backtick behind
."`
means to use the macro ." at execution time. The "then>" you can
imagine as ANS (I hope I make no error in it):

: THEN> STATE @ IF R> COMPILE EXIT THEN ; IMMEDIATE

The backtick in front of "space" is similar to:
['] space

3) Inside the definition of "g" there is two times used a feature
called "coroutines". They are anonymous, so you can only break a
couroutine and execution is continued next time at the code that
follows. The coroutines start with "<\" and and end with "\>". The
points where a coroutine is interrupted are marked with "\\". "\>"
closes the circle - so if the code reaches "\>" next time the
coroutine continues directly after "<\".

Alien?

The second thing, also for 4p and I think a little more traditional:

---------------------
: x dup 5 mod if
: y vector dup .
;then ." Buzz " ;
: z dup 3 mod or: ." Fizz" `space is y ;
: g z x undo y 1+ ;
1 100 times g drop
---------------------

1) "x" falls thrue into "y".
2) ";then" would be similar to the sequence "EXIT THEN"
3) "z" uses "or:" this is similar to

: OR: IF RDROP THEN ;

I do think that "times" or "TIMES" is commonly known as a way to
shorten some programs, so I dont explain it further.

Have Fun!

-Helmar

PS: http://maschenwerk.de/HelFORTH/ is the starting point for HelFORTH
and 4p.

Cesar Rabak

unread,
Mar 10, 2007, 12:30:41 PM3/10/07
to
J Thomas escreveu:

> On Mar 10, 5:37 am, "Doug Hoffman" <dhoff...@talkamerica.net> wrote:
>
>>> Have I done something that offended you? I think I'm detecting a
>>> certain coolness to your tone.
>
>> Earlier you wrote:
>
>>> Which way is brute force? One way you do the
>>> calculations once and never again. The other
>>> way you repeat them every time.
>>> However, if the critical values are 3 5 7 11
>>> then the least common multiple is 1155 .
>>> You aren't going to have a lot of regular repeating
>>> patterns in that. Better to use bit vectors or
>>> simulated bit vectors than try to code it all into
>>> the definition.
>> I wanted to see this "better" definition. Its existence was as I
>> suspected.
>
> ?? Did it sound like I was saying the simulated bit vectors would be
> better than your solution?
>
> I intended to say that the loop-unrolling method would be far worse on
> the larger problem, and the simulated bit-vectors would be better than
> that.
>

It depends how you do the loop unrolling. Accepting by a moment the
reason for needing (like a very limited platform, etc.): do you agree to
write a short piece of Forth code to generate the unrolled code for the
loop as part of solution (this probably done in a hosted environment)?

[snipped]

> So as the problem gets more complex the brute force method gets
> relatively easier. Just tell the computer to work out all the details
> for itself, every time.

And generate a 'static' solution for the bit version version!

my .019999....

J Thomas

unread,
Mar 10, 2007, 4:08:11 PM3/10/07
to
On Mar 10, 12:30 pm, Cesar Rabak <csra...@yahoo.com.br> wrote:
> J Thomas escreveu:

> > I intended to say that the loop-unrolling method would be far worse on
> > the larger problem, and the simulated bit-vectors would be better than
> > that.
>
> It depends how you do the loop unrolling. Accepting by a moment the
> reason for needing (like a very limited platform, etc.): do you agree to
> write a short piece of Forth code to generate the unrolled code for the
> loop as part of solution (this probably done in a hosted environment)?

OK. Where there are 4 outcomes for 3 and 5, for 3 5 7 9 there are 16
outcomes.
Suppose we asrrange them in an array, and use a jump table to choose
which to use.
Then we can make the choice with a number, data. A nibble.

In the simplest case that would be 158 bytes (or maybe 315 bytes) of
data that could come from some sort of mass storage, and there might
be patterns in it to exploit.

On the host we can generate that sequence and look for patterns.


: guts
dup 3 mod 0= 1 and
over 5 mod 0= 2 and or
over 7 mod 0= 4 and or
swap 9 mod 0= 8 and or ;

: actions
316 1 do
i guts .
loop ;

actions 0 0 1 0 2 1 4 0 9 2 0 1 0 4 3 0 0 9 0 2 5 0 0 1 2 0 9 4 0 3 0
0 1 0 6 9 0 0 1 2 0 5 0 0 11 0 0 1 4 2 1 0 0 9 2 4 1 0 0 3 0 0 13 0 2
1 0 0 1 6 0 9 0 0 3 0 4 1 0 2 9 0 0 5 2 0 1 0 0 11 4 0 1 0 2 1 0 4 9 2
0 1 0 0 7 0 0 9 0 2 1 4 0 1 2 0 9 0 4 3 0 0 1 0 2 13 0 0 1 2 0 1 4 0
11 0 0 1 0 6 1 0 0 9 2 0 5 0 0 3 0 0 9 4 2 1 0 0 1 2 4 9 0 0 3 0 0 5 0
2 9 0 0 1 6 0 1 0 0 11 0 4 1 0 2 1 0 0 13 2 0 1 0 0 3 4 0 9 0 2 1 0 4
1 2 0 9 0 0 7 0 0 1 0 2 9 4 0 1 2 0 1 0 4 11 0 0 1 0 2 5 0 0 9 2 0 1 4
0 3 0 0 9 0 6 1 0 0 1 2 0 13 0 0 3 0 0 1 4 2 9 0 0 1 2 4 1 0 0 11 0 0
5 0 2 1 0 0 9 6 0 1 0 0 3 0 4 9 0 2 1 0 0 5 2 0 9 0 0 3 4 0 1 0 2 9 0
4 1 2 0 1 0 0 15 ok

Repeat this sequence 3 times, then do just the first 55 of them.

Extract a nibble, jump into a jump table, execute an xt. As compared
to just execute a command. Or use whole bytes and avoid the nibble
stuff for an extra 157 bytes.

It stands out from the sequence that apart from the last item, the
sequence is a palindrome. So we could cut the length in half if we're
willing to sample it from both ends. Would that be worth doing?
Probably not.

So we'd have something like

CREATE jumptable ' .I , ' Fizz , ' Buzz , ' FizzBuzz , ' Boom , '
FizzBoom , ' BuzzBoom , ' FizzBuzzBoom , ' Zapp , ' FizzZapp , '
BuzzZapp , ' FizzBuzzZapp , BoomZapp , ....

: action
S" our-file" R/O BIN OPEN-FILE ?FILE our-file !
1001 1 DO
i get-action CELLS jumptable + @ EXECUTE DROP
LOOP ;

And depending on how performance goes on your system you could have
something like

: get-action ( i -- i n )
DUP BEGIN DUP 315 > WHILE 315 - REPEAT
0= IF 0 0 our-file @ REPOSITION-FILE ?FILE THEN
HERE DUP 1 our-file @ READ-FILE ?FILE DROP C@ ;

Or you might rather put all the data in memory with one operation and
then cycle through it.

So the code can be reasonably simple, but not ideally readable. In
this particular case, it usually wouldn't be practical to do all this
just to avoid a few MODs. But note that the method generalises better
than the obvious approach. We can generate any sequence whatsoever
once, and then repeat it as many times as required. Even if the specs
that say what the sequence must be take many pages, even if they're
generated at random, provided the sequence is fixed we can generate it
once and then the code will use the same resources and run at the same
speed as the current version.

J Thomas

unread,
Mar 10, 2007, 4:14:45 PM3/10/07
to
On Mar 10, 12:30 pm, Cesar Rabak <csra...@yahoo.com.br> wrote:
> J Thomas escreveu:

> > I intended to say that the loop-unrolling method would be far worse on


> > the larger problem, and the simulated bit-vectors would be better than
> > that.
>
> It depends how you do the loop unrolling. Accepting by a moment the
> reason for needing (like a very limited platform, etc.): do you agree to
> write a short piece of Forth code to generate the unrolled code for the
> loop as part of solution (this probably done in a hosted environment)?

OK. Where there are 4 outcomes for 3 and 5, for 3 5 7 9 there are 16

J Thomas

unread,
Mar 10, 2007, 4:15:28 PM3/10/07
to
On Mar 10, 12:30 pm, Cesar Rabak <csra...@yahoo.com.br> wrote:
> J Thomas escreveu:

> > I intended to say that the loop-unrolling method would be far worse on


> > the larger problem, and the simulated bit-vectors would be better than
> > that.
>
> It depends how you do the loop unrolling. Accepting by a moment the
> reason for needing (like a very limited platform, etc.): do you agree to
> write a short piece of Forth code to generate the unrolled code for the
> loop as part of solution (this probably done in a hosted environment)?

OK. Where there are 4 outcomes for 3 and 5, for 3 5 7 9 there are 16

J Thomas

unread,
Mar 10, 2007, 4:26:32 PM3/10/07
to
On Mar 10, 12:30 pm, Cesar Rabak <csra...@yahoo.com.br> wrote:
> J Thomas escreveu:

> > I intended to say that the loop-unrolling method would be far worse on


> > the larger problem, and the simulated bit-vectors would be better than
> > that.
>
> It depends how you do the loop unrolling. Accepting by a moment the
> reason for needing (like a very limited platform, etc.): do you agree to
> write a short piece of Forth code to generate the unrolled code for the
> loop as part of solution (this probably done in a hosted environment)?

OK. Where there are 4 outcomes for 3 and 5, for 3 5 7 9 there are 16

Cesar Rabak

unread,
Mar 11, 2007, 1:17:26 PM3/11/07
to
J Thomas escreveu:

> On Mar 10, 12:30 pm, Cesar Rabak <csra...@yahoo.com.br> wrote:
>> J Thomas escreveu:
>
>>> I intended to say that the loop-unrolling method would be far worse on
>>> the larger problem, and the simulated bit-vectors would be better than
>>> that.
>> It depends how you do the loop unrolling. Accepting by a moment the
>> reason for needing (like a very limited platform, etc.): do you agree to
>> write a short piece of Forth code to generate the unrolled code for the
>> loop as part of solution (this probably done in a hosted environment)?
>
> OK. Where there are 4 outcomes for 3 and 5, for 3 5 7 9 there are 16
> outcomes.
> Suppose we asrrange them in an array, and use a jump table to choose
> which to use.
> Then we can make the choice with a number, data. A nibble.
[snipped]

> So the code can be reasonably simple, but not ideally readable. In
> this particular case, it usually wouldn't be practical to do all this
> just to avoid a few MODs. But note that the method generalises better
> than the obvious approach.

I agree. I would say that if we are solving the problem on a platform
where this approach is appropriate it would not hurt to have a single
line comment directing the prospective maintainer to a more thorough
document.

> We can generate any sequence whatsoever
> once, and then repeat it as many times as required. Even if the specs
> that say what the sequence must be take many pages, even if they're
> generated at random, provided the sequence is fixed we can generate it
> once and then the code will use the same resources and run at the same
> speed as the current version.
>

Yes, you got my point nicely. Thanks for investing your time creating an
example in code!

Regards,

--
Cesar Rabak

humptydumpty

unread,
Mar 15, 2007, 6:34:07 AM3/15/07
to
Overkill version using a defining word:

1 : .fizz ." fizz" ; : .buzz ." buzz" ;
2
3 ( contorized.act - mem map: n C xt)
4 : n<C? ( a-f) dup @ swap cell + @ < ;
5 : act ( a-) 2 cells + @ execute ;
6 : contorized.act ( xt n --) create 0 , , ,
7 does> >R 1 R@ +! R@ n<C? if 1 else R@ act 0 R@ ! 0 then
8 R> drop ;
9 ' .fizz 3 contorized.act 3do
10 ' .buzz 5 contorized.act 5do
11 : doprint cr 101 1 do 3do 5do and if i . then cr loop ;

- snapshot from gforth block editor.

best regards,
hd

the_gavino_himself

unread,
Jan 8, 2015, 10:24:28 PM1/8/15
to
On Friday, March 2, 2007 at 4:29:39 AM UTC-8, Andrew Haley wrote:
> Cesar Rabak <csr...@yahoo.com.br> wrote:
> > keo...@gmail.com escreveu:
> > [snipped]
>
> >> As I am interested but not yet very proficient in stack based
> >> programming languages, I ask you: how would you solve this problem in
> >> FORTH?
> >>
> > Did you study the solution given in the post # 53?
>
> The code is over-factored, making it hard to read. There is also too
> much stack noise: this problem really doesn't need DUP SWAP etc.
>
> A simple solution is
>
> : bang
> 100 1 do
> i 15 mod 0= if ." FizzBuzz " else
> i 3 mod 0= if ." Fizz " else
> i 5 mod 0= if ." Buzz " else
> i . then then then
> loop ;
>
> There are many more complex and efficient solutions possible.
>
> ( "the numbers from 1 to 100" is ambiguous. I'm assuming a half-open
> interval. )
>
> Andrew.

kick ass!! works well!

HAA

unread,
Jan 15, 2015, 7:22:34 PM1/15/15
to
the_gavino_himself wrote:
> On Friday, March 2, 2007 at 4:29:39 AM UTC-8, Andrew Haley wrote:
> > [...]
> > A simple solution is
> >
> > : bang
> > 100 1 do
> > i 15 mod 0= if ." FizzBuzz " else
> > i 3 mod 0= if ." Fizz " else
> > i 5 mod 0= if ." Buzz " else
> > i . then then then
> > loop ;
> >
> > There are many more complex and efficient solutions possible.
> >
> > ( "the numbers from 1 to 100" is ambiguous. I'm assuming a half-open
> > interval. )
> >
> > Andrew.
>
> kick ass!! works well!

http://rosettacode.org/wiki/FizzBuzz#Pascal

Google what you don't know. Refine it if necessary.



hughag...@gmail.com

unread,
Feb 28, 2015, 11:21:34 PM2/28/15
to
On Saturday, March 3, 2007 at 7:37:16 AM UTC-7, Gerry wrote:
> On 2 Mar, 15:48, "J Thomas" <jethom...@gmail.com> wrote:
> > On Mar 2, 7:29 am, Andrew Haley <andre...@littlepinkcloud.invalid>
> > wrote:
> > > A simple solution is
> >
> > > : bang
> > > 100 1 do
> > > i 15 mod 0= if ." FizzBuzz " else
> > > i 3 mod 0= if ." Fizz " else
> > > i 5 mod 0= if ." Buzz " else
> > > i . then then then
> > > loop ;
> >
> > Your solution looks fine to me too.
> >
> > Your code brings up something for me. I notice those three then then
> > thens. Forth doesn't usually have a way to skip over the rest of a
> > loop and then repeat. We have ways to break out of the loop, but not a
> > command to skip to the end of the loop. So we have ugly things like
> > then then then to do it, both in DO loops and in BEGIN loops.
> >
> > I've never felt the need for an improvement there. Multiple THENs
> > don't actually cause a performance hit, only a very slight compile-
> > time hit. Wil Baden made his THENS command which palliates it.

I don't like nested IF control structures with multiple THEN statements at the end. This is my way of avoiding that:

: <fizzfuzz> ( index -- )
dup 15 mod 0= if ." fizzfuzz" drop exit then
dup 3 mod 0= if ." fizz" drop exit then
dup 5 mod 0= if ." fuzz" drop exit then
. ;

: fizzfuzz ( -- )
101 1 do cr I <fizzfuzz> loop ;

> Nothing wrong with other solutions but a simple solution avoiding
> nested ifs etc uses bit vectors. The constants can be stuck in line
>
> 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
> ;

This was by far the coolest code! I wouldn't have thought of this technique, so I learned something here. I get the impression that Gerry learned to program in the 1980s (on the 6502 or Z80) when multiplication and division were orders of magnitude less efficient than logical operations --- only somebody over 50-years-old would come up with a solution like this.

I will criticize however, that putting the constants in HEX (base-16) obfuscated the code --- why not base-17 or base-18 for total obfuscation? --- the constants are only meaningful in base-2, so BINARY is the base they should have been in for readability. Unless you can convert binary to hex in your head (I can't), then you must have originally written them in binary and then used the Forth REPL to convert them into hex, then manually typed them into the source-code as hex.

BTW: This is the ONLY thread that I've ever seen on comp.lang.forth in which Passaniti wrote Forth code rather than Perl code.

I will also note that this is a pretty easy problem for a job interview --- I find it amazing that anybody could fail to write code at the level of Passaniti's or mine --- not too many would come up with Gerry's solution though.

Michael Barry

unread,
Mar 1, 2015, 12:07:48 AM3/1/15
to
On Saturday, February 28, 2015 at 8:21:34 PM UTC-8, hughag...@gmail.com wrote:
>
> ... Unless you can convert binary to hex in your head (I can't), then
> you must have originally written them in binary and then used the Forth
> REPL to convert them into hex, then manually typed them into the
> source-code as hex.
>

You surprise me with your modesty, Hugh. Binary to Hex and Binary to Octal
are ridiculously simple in either direction. I _do_ trip occasionally on
Hex to Octal though, unless it's good old-fashioned split-octal! ;-)

Mike

Elizabeth D. Rather

unread,
Mar 1, 2015, 2:57:29 AM3/1/15
to
hex deadbeef dup dup decimal u. octal u. binary u. 3735928559
33653337357 11011110101011011011111011101111 ok

In one of my first programming jobs I inherited a large Fortran program
that included two subroutines, OCTDEC (which converted a number from
octal to decimal) and its converse, DECOCT. Realizing that all the
numbers they were working with were actually binary by the time they got
them, I deleted both subroutines, and nobody ever noticed they were
gone, because they didn't actually change anything.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

Doug Hoffman

unread,
Mar 1, 2015, 7:53:42 AM3/1/15
to
On 2/28/15 11:21 PM, hughag...@gmail.com wrote:

> : <fizzfuzz> ( index -- )
> dup 15 mod 0= if ." fizzfuzz" drop exit then
> dup 3 mod 0= if ." fizz" drop exit then
> dup 5 mod 0= if ." fuzz" drop exit then
> . ;
>
> : fizzfuzz ( -- )
> 101 1 do cr I <fizzfuzz> loop ;

Another way.
-Doug

: ?type ( s idx n -- f )
mod 0= if count type 0 then ;

: fizzfuzz
101 1 do cr
c" fizz" i 3 ?type
c" fuzz" i 5 ?type
and if i . then loop ;



Ron Aaron

unread,
Mar 1, 2015, 8:06:17 AM3/1/15
to
Why not? Here's the way I did it in 8th. Don't think it will be too
surprising for ANS users either. Plenty of room for making it
more clever, but why bother?

\ for numbers 1..100, print "Fizz" if multiple of 3,
\ "Buzz" if multiple of 5, and "FizzBuzz" if multiple of both -
\ otherwise, print the number
\ Uses only the stack, no vars

with: n

: num? \ n f -- )
if drop else . then ;

\ is m mod n 0? leave the result twice on the stack
: div? \ m n -- f f
mod 0 = dup ;

: fizz? \ n -- n f
dup 3
div? if "Fizz" . then ;

: buzz? \ n f -- n f
over 5
div? if "Buzz" . then or ;

\ print a message as appropriate for the given number:
: fizzbuzz \ n --
fizz? buzz? num?
space ;

\ iterate from 1 to 100:
' fizzbuzz 1 100 loop
cr bye

Doug Hoffman

unread,
Mar 1, 2015, 10:31:41 AM3/1/15
to
On 3/1/15 8:06 AM, Ron Aaron wrote:

> with: n
>
> : num? \ n f -- )
> if drop else . then ;
>
> \ is m mod n 0? leave the result twice on the stack
> : div? \ m n -- f f
> mod 0 = dup ;
>
> : fizz? \ n -- n f
> dup 3
> div? if "Fizz" . then ;
>
> : buzz? \ n f -- n f
> over 5
> div? if "Buzz" . then or ;
>
> \ print a message as appropriate for the given number:
> : fizzbuzz \ n --
> fizz? buzz? num?
> space ;
>
> \ iterate from 1 to 100:
> ' fizzbuzz 1 100 loop

Yes, 3 mod and 5 mod handle the 15 mod case. I prefer that as well.

I was pleased to see 8th do something like the following (though not
really liking having to declare " with: n" ):

with: n

: add. + . ;

100 12.5 add. \ => 112.50000


But then a bit disappointed to get this error:

"hello" "world" add. \ => "Expected Number, got String"

Or perhaps I'm doing something wrong.

-Doug

Ron Aaron

unread,
Mar 1, 2015, 12:45:48 PM3/1/15
to


On 3/1/15 17:24, Doug Hoffman wrote:

> I was pleased to see 8th do something like the following (though not
> really liking having to declare " with: n" ):

You don't have to say "with: n", its just a shortcut to keep from having
to prefix words with "n:" if they are number-class words.


>
> with: n
>
> : add. + . ;
>
> 100 12.5 add. \ => 112.50000
>
>
> But then a bit disappointed to get this error:
>
> "hello" "world" add. \ => "Expected Number, got String"
>
> Or perhaps I'm doing something wrong.

Yes :)

The + should be s:+

You told 8th that you expect to use the n: class, and there's a + there
-- so it's used.

What you intend is to use the string +, so "s:+" is what you need.

Alternatively, if you intended to handle both numbers and strings with
the same word, you could have said:

: add. l: + . ;

That gives "late-binding" semantics, so that "+" is looked up (every
time) at runtime, so then
2 4 add. gives 6 , and
"hi" " there" add. gives "hi there"


hughag...@gmail.com

unread,
Mar 1, 2015, 5:05:48 PM3/1/15
to
I'm not good at doing arithmetic in my head --- fortunately, I don't have to, because I own a computer!

What I liked about Gerry's solution was that it reminded me of the ladder-diagrams used in PLCs (I've never actually used a PLC, although I did read up on them when I was working at Testra and there was talk about implementing a PLC around the MiniForth). Also, in micro-controllers a simple strategy is the "paced loop" --- this is a loop that is tied to a timer so it executes at a steady pace (perhaps every 10 milliseconds, or 100 times per second), so events get "clocked" to occur at specific times within a time-frame (perhaps 20 or 30 seconds). These are used for motion-control when you have a machine (typically in a factory) performing some function repeatedly, and several moving parts have to be synchronized so they don't bash into each other.

Anyway, making his bit-vector constants hexadecimal seemed like obfuscation to me --- the algorithm would have been clear even without comments if the bit-vectors had been in binary, but the hex numbers likely seemed to most people to be "magic numbers" that he had pulled out of the air (it seemed that way to me, until I noticed the 1 RSHIFT and realized that his PROBE was lining up with the bits in the masks).

hughag...@gmail.com

unread,
Mar 1, 2015, 6:37:55 PM3/1/15
to
On Sunday, March 1, 2015 at 12:57:29 AM UTC-7, Elizabeth D. Rather wrote:
> hex deadbeef dup dup decimal u. octal u. binary u. 3735928559
> 33653337357 11011110101011011011111011101111 ok

You forgot to put the Forth system back into DECIMAL again, so you will get an "undefined word" abort the next time that the REPL encounters a number (or, worse, you will get the wrong number in the case that the number can be interpreted as binary and decimal was expected). You should have used B. from my novice-package to print out your number in binary, as it restores BASE automatically.

One of the more screw-ball aspects of ANS-Forth is that QUIT (called by ABORT) doesn't put the system back into a rational state. If the system state (such as BASE, or the word-list stack) is screwed up, it will still be screwed up after the abort --- it remains screwed up until the user figures out what is wrong and explicitly fixes the problem.

> In one of my first programming jobs I inherited a large Fortran program
> that included two subroutines, OCTDEC (which converted a number from
> octal to decimal) and its converse, DECOCT. Realizing that all the
> numbers they were working with were actually binary by the time they got
> them, I deleted both subroutines, and nobody ever noticed they were
> gone, because they didn't actually change anything.

You describe this as a "programming job" --- but there is nobody in the world who would consider this to be programming experience --- you are not a programmer.

I mentioned before that this was the only thread on C.L.F. in which I had seen Passaniti provide Forth code rather than Perl code. FizzBuzz is apparently a common job-interview question, so the solution is readily available on the internet (Google "FizzBuzz") --- he faked it, then pretended to be so good at programming that he would be the job interviewer rather than the job applicant.

Two years later when I posted my symtab program, Passaniti said that it "sucks" --- you described this as "perceptive and technically sound" and denounced me for being "homophobic."

EVERY example of Forth code that you have posted on C.L.F. was copied directly out of the "Starting Forth" book --- you aren't even good at faking, as you use the same book every time, whereas Passaniti was at least smart enough to use Google to find a variety of sources to copy from.

> Cheers,
> Elizabeth

Go to hell, poser!

Doug Hoffman

unread,
Mar 2, 2015, 5:29:39 AM3/2/15
to

Thanks for the explanation, Ron. I need to read the documentation more
carefully!

I like what I see in 8th so far and am continuing to experiment with it.
Your example code snippets posted here are helpful.

-Doug

Ron Aaron

unread,
Mar 2, 2015, 6:06:52 AM3/2/15
to

On 03/02/2015 12:29, Doug Hoffman wrote:
>
> Thanks for the explanation, Ron. I need to read the documentation more
> carefully!

Don't we all?!?


> I like what I see in 8th so far and am continuing to experiment with it.
> Your example code snippets posted here are helpful.

Thank you; however, since this group is really for ANS Forth I don't
often post here. There is a lot of (useful) activity on the 8th forum,
you should go there for 8th-specific information.

Doug Hoffman

unread,
Mar 2, 2015, 10:12:51 AM3/2/15
to
On 3/2/15 6:06 AM, Ron Aaron wrote:
> ... however, since this group is really for ANS Forth

Is it? Not counting the obvious no-relation-whatsoever-to-Forth troll
code, I see a lot of non-ANS stuff, yet clearly a Forth derivative.

> I don't often post here.

I can tell you that *I* don't mind the 8th posts here at all. I find
them enlightening and (Forth) thought provoking.

> There is a lot of (useful) activity on the 8th forum,
> you should go there for 8th-specific information.

Agreed.

-Doug

Mark Wills

unread,
Mar 2, 2015, 11:57:41 AM3/2/15
to
Seconded.

Ron Aaron

unread,
Mar 2, 2015, 12:23:11 PM3/2/15
to
OK, well thanks guys!

Albert van der Horst

unread,
Mar 2, 2015, 5:33:37 PM3/2/15
to
In article <6d9c9d1b-d73e-4959...@googlegroups.com>,
<hughag...@gmail.com> wrote:
>On Sunday, March 1, 2015 at 12:57:29 AM UTC-7, Elizabeth D. Rather wrote:
>> hex deadbeef dup dup decimal u. octal u. binary u. 3735928559
>> 33653337357 11011110101011011011111011101111 ok
>
>You forgot to put the Forth system back into DECIMAL again, so you will
>get an "undefined word" abort the next time that the REPL encounters a
>number (or, worse, you will get the wrong number in the case that the
>number can be interpreted as binary and decimal was expected). You
>should have used B. from my novice-package to print out your number in
>binary, as it restores BASE automatically.
>
>One of the more screw-ball aspects of ANS-Forth is that QUIT (called by
>ABORT) doesn't put the system back into a rational state. If the system
>state (such as BASE, or the word-list stack) is screwed up, it will
>still be screwed up after the abort --- it remains screwed up until the
>user figures out what is wrong and explicitly fixes the problem.

It is an illusion that QUIT could do that in general, e.g.

TASK : ISN'T UNIQUE
OK
DSP@ HERE - ALLOT
DSP@ HERE - ALLOT ? ciforth ERROR # 7 : FULL STACK
1 2 + .
1 ? ciforth ERROR # 7 : FULL STACK
-1000 ALLOT
-1000 ? ciforth ERROR # 7 : FULL STACK
COLD

It is quite useful that QUIT leaves everything as it is except for
the return stack. So it can be used for desperado post mortem
debugging.

Despite the weird name QUIT means "get commands from the console
and execute them".

Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Elizabeth D. Rather

unread,
Mar 2, 2015, 6:01:36 PM3/2/15
to
Yes. It is really important that QUIT not automatically change
environmental things such as BASE. The user needs to be in control.
There are many occasions when one wants to work in a base other than
DECIMAL for a while, such as testing hardware with hex addresses and bit
patterns.

Back in the day, there were systems that automatically set DECIMAL when
entering or leaving a block being interpreted, and today that's commonly
done with files being loaded, but there remain cases when it's
inconvenient, and even this fairly large-scale amount of control isn't
widely popular.

hughag...@gmail.com

unread,
Mar 3, 2015, 2:32:27 PM3/3/15
to
On Monday, March 2, 2015 at 3:33:37 PM UTC-7, Albert van der Horst wrote:
> In article <6d9c9d1b-d73e-4959...@googlegroups.com>,
> <hughag...@gmail.com> wrote:
> >One of the more screw-ball aspects of ANS-Forth is that QUIT (called by
> >ABORT) doesn't put the system back into a rational state. If the system
> >state (such as BASE, or the word-list stack) is screwed up, it will
> >still be screwed up after the abort --- it remains screwed up until the
> >user figures out what is wrong and explicitly fixes the problem.
> ...
> It is quite useful that QUIT leaves everything as it is except for
> the return stack. So it can be used for desperado post mortem
> debugging.

That is not true. ABORT empties the data-stack, largely making "desperado post mortem debugging" impossible.

I think that what happened here, is that Charles Moore knew that the system needed to be put back into a rational state after an abort, so he wrote ABORT to empty the data-stack --- but he simply forgot to put BASE, CONTEXT and CURRENT back into a rational state --- after he left, nobody had the nerve to fix any of his bugs, so this bug is still with us 40 years later and has become a part of the ANS-Forth standard.

It is clearly a bug to have the system in a unknown state after a crash --- ABORT should put the user back in the REPL in a rational state --- it would make sense for ABORT to save a snapshot of the system somewhere that can be examined post-mortem, but the user would look at that snapshot at his leisure, rather than be put back into the screwed-up state at the REPL.

Also, why does ANS-Forth have QUIT at all? The only purpose of QUIT is to be called by ABORT, but QUIT is never called by the user under any circumstance. I think Charles Moore factored QUIT out of ABORT because he likes to factor the heck out of his code, but nobody on the ANS-Forth committee realized that QUIT is never used outside of ABORT and does not need to be exposed to the user.

hughag...@gmail.com

unread,
Mar 3, 2015, 3:34:55 PM3/3/15
to
On Monday, March 2, 2015 at 4:01:36 PM UTC-7, Elizabeth D. Rather wrote:
> On 3/2/15 12:33 PM, Albert van der Horst wrote:
> > It is quite useful that QUIT leaves everything as it is except for
> > the return stack. So it can be used for desperado post mortem
> > debugging.
> >
> > Despite the weird name QUIT means "get commands from the console
> > and execute them".
>
> Yes. It is really important that QUIT not automatically change
> environmental things such as BASE. The user needs to be in control.
> There are many occasions when one wants to work in a base other than
> DECIMAL for a while, such as testing hardware with hex addresses and bit
> patterns.

This is typical liberal rhetoric --- you say: "The user needs to be in control." --- this is a straw-man argument, to imply that I was arguing that the user not be in control.

Of course the user can set system state such as BASE and the wordlist-stack "for a while" --- but after an abort, then the system has to be put back in a rational state --- obviously, the user lost control of the system, which is why it crashed.

> Back in the day, there were systems that automatically set DECIMAL when
> entering or leaving a block being interpreted, and today that's commonly
> done with files being loaded, but there remain cases when it's
> inconvenient, and even this fairly large-scale amount of control isn't
> widely popular.

It is common to temporarily switch to a different BASE (as Gerry did in his program) or to temporarily switch to a different wordlist-stack or CURRENT wordlist (such as when switching to ASSEMBLER and/or writing an assembly-language macro). If the system crashes when it is in this temporary unusual state, then ABORT puts the user back in the REPL without cleaning up the mess. The user doesn't know that the system was in a temporarily unusual state when it crashed, or what that state was --- the system will seem to be behaving in an irrational manner --- I know how to manually recover the system, but most novices are going to think the crash is irrepairable (especially if the wordlist-stack doesn't include FORTH-WORDLIST), and they are going to shut Forth down and reboot it from scratch (or just give up and go back to programming in C).

The typical way that the system is restored is like this:
ONLY FORTH DEFINITIONS DECIMAL

The ANS-Forth document (16.6.2.1965) says this about ONLY:

"Set the search order to the implementation-defined minimum search order. The minimum search order shall include the words FORTH-WORDLIST and SET-ORDER."

This means that, after the ONLY, the word FORTH may not be in the search-order (also, FORTH is in an extension wordset; it is not part of the core-wordset, so it is not guaranteed to be available at all), in which case the above method will fail with "undefined word" when it gets to the FORTH (that would totally baffle 99% of the users!).

So, the only reliable method is:
ONLY FORTH-WORDLIST SET-ORDER DEFINITIONS DECIMAL

I'll bet there isn't one Forth programmer in a hundred who knows this --- although there may not even be 100 Forth programmers remaining on the planet (pretty much everybody abandoned Forth in 1994 because ANS-Forth was no good).

As I said before, error-recovery is a "screwball aspect" of ANS-Forth --- the entire ANS-Forth standard is a mish-mash of nonsense --- this is what happens when you put a salesperson in charge of standardizing a programming language!

Elizabeth Rather says:
> Back in the day, there were systems that automatically set DECIMAL when
> entering or leaving a block being interpreted, and today that's commonly
> done with files being loaded, but there remain cases when it's
> inconvenient, and even this fairly large-scale amount of control isn't
> widely popular.

This is "commonly done"??? Why isn't this standardized? The ANS-Forth standard doesn't standardize anything, so we are left with a mish-mash of Forth systems that may or may not do one thing or another --- it is up to the user to wonder what is "commonly done" and whether his compiler does this or not, pretty much the same as if there were no standard at all.

Also, INCLUDE or LOAD should not set DECIMAL anyway --- only ABORT should set DECIMAL in order to get back into a rational state --- but INCLUDE or LOAD should not change the state of the system.

Elizabeth Rather says:
> Back in the day, there were systems that automatically set DECIMAL when
> entering or leaving a block being interpreted, and today that's commonly
> done with files being loaded, but there remain cases when it's
> inconvenient, and even this fairly large-scale amount of control isn't
> widely popular.

In MFX I had a situation in which I wanted the CONTEXT and CURRENT to be set prior to the INCLUDE of a file. I had HOST and TARG modes for code that ran on the desktop-computer at compile-time, and code that ran on the micro-controller at run-time. A lot of the code could compile under either HOST or TARG mode however (not all of it, but some libraries of arithmetic functions and other general-purpose code). I would execute HOST then do an INCLUDE of the source-file, then execute TARG and do an INCLUDE of the same source-file, and get all of this code duplicated. This isn't a problem --- INCLUDE or LOAD don't change CONTEXT and CURRENT --- only ABORT should change CONTEXT and CURRENT to get them back into a rational state (but it fails to do this under ANS-Forth).

Alex McDonald

unread,
Mar 3, 2015, 3:43:19 PM3/3/15
to
on 03/03/2015 20:34:48, hugy wrote:

>
> So, the only reliable method is:
> ONLY FORTH-WORDLIST SET-ORDER DEFINITIONS DECIMAL
>
> I'll bet there isn't one Forth programmer in a hundred who knows this

Including you it would appear, since it's actually

ONLY FORTH-WORDLIST 1 SET-ORDER ...

Apart from that, it's a valid point.

Gerry Jackson

unread,
Mar 3, 2015, 3:56:19 PM3/3/15
to
On 01/03/2015 04:21, hughag...@gmail.com wrote:

Sorry about the delay in replying, I'm just catching up.

>> 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
>> >;
> This was by far the coolest code! I wouldn't have thought of this
> technique, so I learned something here. I get the impression that
> Gerry learned to program in the 1980s (on the 6502 or Z80)

No, nothing as modern as those processors. I think it was my hardware
design background e.g. use of shift registers.

> when
> multiplication and division were orders of magnitude less efficient
> than logical operations --- only somebody over 50-years-old would
> come up with a solution like this.

You might be correct :-)

> I will criticize however, that putting the constants in HEX
> (base-16) obfuscated the code --- why not base-17 or base-18 for
> total obfuscation? --- the constants are only meaningful in
> base-2, so BINARY is the base they should have been in for
> readability. Unless you can convert binary to hex in your head
> (I can't), then you must have originally written them in binary
> and then used the Forth REPL to convert them into hex, then
> manually typed them into the source-code as hex.

I can't remember. I do know that a processor I helped design had 24 bit
instructions with fields of 3, 2, 1, 3, 5, 5, 5 bits. When inputting
those manually you soon learned to instantly recognise binary patterns
up to 31. Hex is easy.

I was criticised before about unreadability and accepted the criticism,
I just didn't think about it when I posted the code, it wasn't
deliberate obfuscation - if it was I wouldn't have named the constants.

--
Gerry

hughag...@gmail.com

unread,
Mar 3, 2015, 4:50:52 PM3/3/15
to
Okay, you got me on that one --- I would have been baffled and would have likely ended up just rebooting the whole system.

This is what the ANS-Forth document says about SET-ORDER:

"( widn ... wid1 n -- )
Set the search order to the word lists identified by widn ... wid1. Subsequently, word list wid1 will be searched first, with word list widn searched last. If n is zero, empty the search order. If n is minus one, set the search order to the implementation-defined minimum search order. The minimum search order shall include the words FORTH-WORDLIST and SET-ORDER. A system shall allow n to be at least eight."

So, your code doesn't work either. You end up with a word-list stack containing only FORTH-WORDLIST. That doesn't necessarily include the minimum search order that ONLY put there. For example, consider this in VFX:

only ok
order
ROOT EXTERNALS ROOT
Current: FORTH
ok

Your method will get rid of ROOT and EXTERNALS, which are presumably needed. Also, what is the deal with having ROOT in there twice???

The best solution would be to use something similar to my PUSH-CONTEXT in the novice-package --- but this won't work because all the novice-package code is in the FORTH-WORDLIST wordset, so none of this will be available if FORTH-WORDLIST isn't in the word-list stack.

As I said before, error-recovery is one of the more screwball aspects of ANS-Forth --- and it wouldn't have been difficult to standardize a solution that made sense --- five minutes of thinking would have been adequate (but that is 5 more minutes of thinking than went into the entire standard).

Here is another solution that doesn't work:

only get-order forth-wordlist swap 1+ definitions decimal
Err# -13 ERR: Undefined word.
-> only get-order forth-wordlist swap 1+ definitions decimal
^
SWAP (and 1+ too) ar in Forth, so they aren't available after ONLY. Also, GET-ORDER isn't guaranteed to be available after ONLY (only FORTH-WORDLIST and SET-ORDER are guaranteed after ONLY).

All in all, the only thing that will work is the common solution:

ONLY FORTH DEFINITIONS DECIMAL

But this only works if FORTH is available --- it isn't guaranteed to be available because it is in an extension wordset --- but this extension wordset is commonly provided (notice that we are back to relying on Elizabeth Rather's word "commonly").

Alex McDonald

unread,
Mar 3, 2015, 5:08:33 PM3/3/15
to
on 03/03/2015 21:50:49, hugh wrote:

>
> So, your code doesn't work either.

My code? It was your code I was correcting.

hughag...@gmail.com

unread,
Mar 3, 2015, 5:09:22 PM3/3/15
to
On Tuesday, March 3, 2015 at 1:56:19 PM UTC-7, Gerry wrote:
> On 01/03/2015 04:21, hughag...@gmail.com wrote:
>
> Sorry about the delay in replying, I'm just catching up.
>
> >> 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
> >> >;
> > This was by far the coolest code! I wouldn't have thought of this
> > technique, so I learned something here. I get the impression that
> > Gerry learned to program in the 1980s (on the 6502 or Z80)

> No, nothing as modern as those processors. I think it was my hardware
> design background e.g. use of shift registers.

I figured as much --- that is why I said that your code reminded me of hardware clocking.

> I do know that a processor I helped design had 24 bit
> instructions with fields of 3, 2, 1, 3, 5, 5, 5 bits. When inputting
> those manually you soon learned to instantly recognise binary patterns
> up to 31. Hex is easy.

You helped design a processor? I thought I was the only one on C.L.F. (the MiniForth) --- I don't count Bernd Payson and his B16 because it only has 32 instructions, so it is a toy with no practical use.

On the MiniForth, each opcode was divided into 5 fields, similar to your 7 fields. My assembler would pack the instructions together into the opcodes, trying to get 5 instructions packed together (if fewer instructions could be packed into a single opcode, NOP instuctions would fill in the other fields). My assembler would rearrange the instructions to minimize how many opcodes got assembled, while still guaranteeing that the program did the same thing as if each opcode had only one instruction in it and the opcodes were in the same order as the instructions in the source-code. This both made the program smaller (fewer opcodes assembled) and sped it up (each opcode took 1 clock cycle, no matter if it had 1, 2, 3, 4 or 5 instructions packed into it). Did you do anything similar?

Getting back to the subject of word-lists, we have this 1993 thread:
https://groups.google.com/forum/#!topic/comp.lang.forth/olO-VHXJa1Q%5B126-150-false%5D
Here Ray Duncan describes the Ragsdale's ONLY scheme as "severely brain-damaged" --- nothing has changed in 20 years! --- brain-damage is, unfortunately, a permanent condition.
Message has been deleted

hughag...@gmail.com

unread,
Mar 3, 2015, 5:18:53 PM3/3/15
to
This is your code:

ONLY FORTH-WORDLIST 1 SET-ORDER ok
order
FORTH
Current: FORTH
ok

You expected it to be the same as this:

only forth definitions ok
order
FORTH EXTERNALS ROOT
Current: FORTH
ok

Your code doesn't work because you accidentally lost ROOT and EXTERNALS. This is on VFX --- what ONLY does is not really standardized (the only requirement is that whatever word-list(s) are on the word-list stack, they should contain the words FORTH-WORDLIST and SET-ORDER) --- there is no standardization as to what word-lists ONLY puts on the word-list stack, so VFX can have ONLY put ROOT and EXTERNALS there, although ROOT and EXTERNALS aren't ANS-Forth standard.

Alex McDonald

unread,
Mar 3, 2015, 5:41:12 PM3/3/15
to
on 03/03/2015 22:18:54, wrote:
> On Tuesday, March 3, 2015 at 3:08:33 PM UTC-7, Alex McDonald wrote:
>> on 03/03/2015 21:50:49, hugh wrote:
>>
>> >
>> > So, your code doesn't work either.
>>
>> My code? It was your code I was correcting.
>
> This is your code:
>
> ONLY FORTH-WORDLIST 1 SET-ORDER ok

No, it's your erroneous code

ONLY FORTH-WORDLIST SET-ORDER

that I corrected. Note you missed the 1 (the wid count) after
FORTH-WORDLIST.

Whatever. There's an issue but most Forths avoid it by including FORTH in
the minimal search order. You might want to create an RFD for ONLY.

Albert van der Horst

unread,
Mar 3, 2015, 6:55:12 PM3/3/15
to
In article <40b9f1bb-0d69-4498...@googlegroups.com>,
<hughag...@gmail.com> wrote:
>On Monday, March 2, 2015 at 3:33:37 PM UTC-7, Albert van der Horst wrote:
>> In article <6d9c9d1b-d73e-4959...@googlegroups.com>,
>> <hughag...@gmail.com> wrote:
>> >One of the more screw-ball aspects of ANS-Forth is that QUIT (called by
>> >ABORT) doesn't put the system back into a rational state. If the system
>> >state (such as BASE, or the word-list stack) is screwed up, it will
>> >still be screwed up after the abort --- it remains screwed up until the
>> >user figures out what is wrong and explicitly fixes the problem.
>> ...
>> It is quite useful that QUIT leaves everything as it is except for
>> the return stack. So it can be used for desperado post mortem
>> debugging.
>
>That is not true. ABORT empties the data-stack, largely making
>"desperado post mortem debugging" impossible.

ABORT may, but QUIT must not (according to the ISO standard.)

<SNIP>
>Also, why does ANS-Forth have QUIT at all?

An interesting question in view of the fact that it is the
user command - execute loop.

It is like
why is there Forth?
why are we here?

Paul E Bennett

unread,
Mar 3, 2015, 6:57:54 PM3/3/15
to
hughag...@gmail.com wrote:

[%X]

>> I do know that a processor I helped design had 24 bit
>> instructions with fields of 3, 2, 1, 3, 5, 5, 5 bits. When inputting
>> those manually you soon learned to instantly recognise binary patterns
>> up to 31. Hex is easy.
>
> You helped design a processor? I thought I was the only one on C.L.F. (the
> MiniForth) --- I don't count Bernd Payson and his B16 because it only has
> 32 instructions, so it is a toy with no practical use.

In the comparison table I built, the B16 has 40 instructions, and the B16
Small has 36 instructions. I used Bernd's own web-page describing both
processors at <http://bernd-paysan.de/b16.html

Vic Plochota suggested a Forth with 18 instructions and Brinkhoff (sorry,
forgot first name) suggested a minimum of 12 instructions (although not
necessarily for CPU hardware).


--
********************************************************************
Paul E. Bennett IEng MIET.....<email://Paul_E....@topmail.co.uk>
Forth based HIDECS Consultancy.............<http://www.hidecs.co.uk>
Mob: +44 (0)7811-639972
Tel: +44 (0)1392-426688
Going Forth Safely ..... EBA. www.electric-boat-association.org.uk..
********************************************************************
It is loading more messages.
0 new messages