Question

I am trying to teach myself Forth by doing Project Euler exercises. I've looked into several tutorials, but I couldn't find any definitive guide as to how to position keywords / control structures. Emacs forth-mode seems to have some idea about how the code should be formatted, but I am not really convinced about what I see :) So, for example, the code below:

: euler1
    0 1000 0
    do i dup 3 mod -rot 5 mod -rot -rot * 0=
        if i + then
    loop ;

Does it make sense to format it this way? Where'd you put conditions? If there's any kind of style guide / a collection of examples, which, you believe, are properly formatted, could you please refer me to that example?

Was it helpful?

Solution 2

If you mean formatting in the sense of where the whitespace should go, your example seems pretty reasonable; indentation for loops and words makes the code fairly readable for people more used to languages where indentation or brackets are required.

My style preference would be more like the code below, but I am not sure what you code is doing so I may have arranged it in a way that does not make perfect sense. In general I would put the conditions for a conditional on a new line along with the keyword, and if the condition is complicated I would factor it out into its own word (add-i? below).

: add-i?
    dup 3 mod -rot 5 mod -rot -rot * 0= ;

: euler1
    0 
    1000 0 do
        i add-i? if 
            i + 
        then
    loop ;

It can be a little unintuitive to have the starting keyword of a conditional block or loop at the end of the first line, but I sort of think of it as a visual cue similar to the way Python uses a : to enter an indented region (or how Visual Basic uses Then (see the second example)), e.g.

if true:
    print("True")

The thens and loops are the equivalents of closing curly brackets or keywords like End If in Visual Basic e.g.

 If True Then
     MsgBox "True"
 End If

(which is a slightly confusing example as the use of Then is different to its use in Forth)

OTHER TIPS

As a beginner, please do yourself a favor and thoroughly comment your code, like:

1       : euler1        ( -- n )
2               0               ( n )
3               1000 0          ( n HI-limit LO-index )
4               DO              ( n )
5                       I DUP   ( n i i )
6                       3 MOD   ( n i mod3 )
7                       -ROT    ( mod3 n i )
8                       5 MOD   ( mod3 n mod5 )
9                       -ROT    ( mod5 mod3 n )
10                      -ROT    ( n mod5 mod3 )
11                      *       ( n mod5*mod3 )
12                      0=      ( n flag )
13                      IF
14                              i +     ( n+i )
15                      THEN
16              LOOP
17      ;

You'll see that:

  1. at lines 9,10 -ROT -ROT could be resumed with simple ROT
  2. it is not necessary to use ROT -ROT that juggle 3 items on data stack and could just simple use SWAP like: 3 MOD SWAP 5 MOD
  3. more yet you could avoid stack juggling and write: I 3 MOD I 5 MOD

I would write that:

: div?          ( a b -- f  ; b DIVIDES a ? )
        mod 0=
;
: euler1.1      ( -- n )
        0
        1000 0
        DO      ( n )
                I 3 div? I 5 div? OR
                IF
                        I +
                THEN
        LOOP
;

A lot of the forth written today has C inspired formatting.

The classic, and (I think) clearest way to format Forth is to align corresponding or related things.

When corresponding pieces are aligned the code is easier to walk through and may expose a common factor that can be pulled out into its own word. Here's a snippet from my forth-crypt library.

\ RSA Digital Signature ( Notice the spacing )
 : signat (   addr-c # -- s )  djb2a      pub-key N rsa   ;
 : verify ( s addr-c # -- f )  djb2a swap prv-key N rsa = ;

It's now easy to see that ( N rsa ) is a factor that could be broken-out into another word if desired.

Another style point is, lookahead/parsing words should be flush to the left and regular postfix words are flush to right like so:

: P@REL (         -- PadLast )                      FF CR1! DATA@ REL ;
: P!REL ( PadLast --         )                      FF CR1! DATA! REL ;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top