Tips for golfing in Forth

10

2

What general tips do you have for golfing in Forth (and its clones)? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Forth (e.g. "remove comments" is not an answer).

cat

Posted 2016-01-02T21:06:51.050

Reputation: 4 989

Answers

3

Avoid explicit loops at all costs

Forth has two looping constructs, x y do ... loop, and lesser known [begin] ... [until] x y where x and y are values for limit and index, or conditions to be noted, respectively.

These are very slow, very wordy (haha) and overall rather bloaty, so only use them if you need to.

Instead, like a proper functional language (which Forth is, really), one should prefer recursivity over explicit loops because it tends to be shorter and makes a better use of the language.

cat

Posted 2016-01-02T21:06:51.050

Reputation: 4 989

3

Leave the junk

Programs aren't required to be stack-safe.

Instead, they can leave extra junk on the stack if it saves you bytes. In Forth, the "return value" is leaving something on top of the stack. It probably doesn't matter what junk you have below that on the stack unless you're using recursion and stack depth matters.

mbomb007

Posted 2016-01-02T21:06:51.050

Reputation: 21 944

2

Forth has a lot of cool builtin words, many of which are helpful for crafting the sorts of algorithms found on PPCG.

A bad but illustrative example of this are the words for increment (1+) and decrement (1-). They save a byte over writing 1 + to increment the top of the stack.

Additonally, here is a handy list of many (probably not all) words found in modern distributions like gforth.

cat

Posted 2016-01-02T21:06:51.050

Reputation: 4 989

1

Try using various double-cell words

This includes double-cell arithmetic, bitwise, number comparison and stack manipulation words. Double-cell integer literals also count.

  • Appending a period on an integer literal (e.g. 1.) gives a double-cell literal. For small numbers, it means pushing an extra zero at the cost of only one byte.
  • m+ = 0 d+ ~= under+ (not exactly, since overflow may occur.)
  • d2* and d2/ could be used to extract/push one bit from a word.
  • a b c d d= = a c = b d = and
  • a b c d d<> = a c <> b d <> or
  • Words that copy two numbers at once, e.g. 2dup, 2over, 2tuck have a good chance to win over local variables.

Bubbler

Posted 2016-01-02T21:06:51.050

Reputation: 16 616

1

Watch the stack

When writing your code, pay attention to what happens on the stack at each command. I usually draw it out as I go, like so:

6       6
7       7 6
* DUP   42 42

As you go like this, you may find it easier to recognize when you can make use of stack operations like ROT, -ROT, 2DUP, 2OVER, etc...

mbomb007

Posted 2016-01-02T21:06:51.050

Reputation: 21 944

0

(gforth-specific) Utilize the separate floating-point stack

gforth has a separate stack for floating-point numbers. Even if you're only dealing with integers, offloading some work and storage to the FP stack may result in shorter code overall, either by avoiding explicit stack manipulation or using FP-specific operations not available on the main stack. Returning to the FP stack is also a perfectly valid option (except when the task is to return a boolean).

  • FP-related operations (arithmetic, stack manipulation) usually cost one more byte each (the f prefix, as in f+, fdup etc.). FP number literals also cost one more byte (postfix e, as in 1e), but some numbers can tie or even save a byte (e.g. 1e3 instead of 1000).
  • Some operations are missing on the FP stack, probably the most painful being 1+ and 1-.
  • FP stack's unique operations include f** (power), falog (power of 10), fsqrt, and various exp, log, trigonometry-related ones.
  • You can move numbers between main and FP stacks via s>f (single to float), f>s (float to single), d>f (double to float), f>d (float to double). At times, the "double" variations may be used to produce or consume an extra zero.

Bubbler

Posted 2016-01-02T21:06:51.050

Reputation: 16 616