Learn from these smart tips and hints about Forth programming. These
tips are not guaranteed to work for you, but hopefully you'll learn a
thing or two.
If you commonly work with arrays in Forth, here's a simple array notation that you might find useful.
Then I create an index operator starting with the } symbol, usually followed by a letter or short descriptor that describes the type of array being indexed. Something like }n or }name. So a reference to an array element would look like this:
for the 6th element in the array. The operator simply leaves the address of the desired element on the stack. For example, lets say I was using a 40 character max length for my name array, and a standard counted string could be stored in each 40 character field. Then the index operator definition would look like this:
I always load a small dictionary that extends the language when I'm working, and when I run my programs. In that extended dictionary I define a number of commonly used indexing operators, like }c to index character array elements, } to index cell array elements, }f to index floating point array elements, and }" to indicate counted string array elements. See if the idea doesn't work for you. For me it makes my Forth code seem easier to read, when the array work looks much like that in any other computer language.
I program in both a forth I wrote years ago, and gforth. In both of these I took advantage of the extensibility of the language to create a simple programing environment that provides the ease of use of the old block file system, even when working with forth within another operating system (like Linux).
1 text ( get the file name into pad)
4 (edit) c! ( reset the length of the edit command, including blank)
pad count (edit) count + swap cmove ( append file name to command)
pad c@ (edit) c@ + (edit) c! ( set the total length of the cmd)
Next, I created a simple edit command named ed. Simply typing ed puts me in edit mode, and when I exit I can try out the code. Simple, but very handy and effective. The ed command looks like this:
: ed ( -- ) (edit) count system ; ( tell system to run edit command)
This is the code for gforth. About the only thing you might need to do for your forth is to create the text word (it just uses word and copies here to pad), and replace system with whatever command lets you pass a string to the operating system.
Return To Index
Title: An Easy Reload Command For Gforth
From web browsing I've noticed that others who've tried gforth have been a bit puzzled on how to avoid getting all the redefined messages when re-compiling modified code. I dealt with the problem by creating an open word that stores my desired program code file in an edit string, and a reload string. Then I created a reload word that loads an empty.fs file (which forgets all my compiled code), then reloads my program file. The empty.fs file looks like this:
I needed a string to hold the file name I was working on
create (reload) 80 allot
The open command, described in the previous hint, also has this:
: open ( -- )
pad (reload) pad c@ 1+ cmove
The reload command looks like this:
: reload ( -- )
s" empty.fs" included
(reload) count included
It use, I enter gforth including my extended dictionary that has the text word, the open, word, the reload word, and many other words I find useful. Then I enter the open word followed by the program file I want to work on. After each editing sequence, I simply type reload to forget and re-compile my code for testing. Edit, then reload, then test.
If you don't have a text word, it looks like this:
: text ( mark -- ) \ gather following text till mark and put into pad
word count dup >r pad 1+ swap cmove r> pad c!
Return To Index
Title: Local Variables
This is my weigh-in on the use of local variables in Forth. I have quite a bit of experience using Forth in that I've used it both at home and on the job for over 20 years. Even so, I'm sure that many Forth experts would consider my code to be sub-optimal quality.
But I have created some pretty functional code that has been used for years and has served well, so I figure I can weigh in as well as the next guy. To be honest, I've been a bit disconnected from the Forth community for some time, having used mostly a version of Forth of my own making. Creating my own version wasn't an exercise or act of vanity, but the solution for the need of a version of Forth that could give full floating point support. To that end, I wrote a version of Forth using the c language, and patterned it after the Forth 79 standard.
Since Forth is so extensible, I've been able to solve my problems nicely with that version for all these years, so I haven't paid much attention to where Forth was going. Recently I've been trying to get caught up a bit by working with the GNU gforth language. I've written a review of my experiences with gforth, which you can read at the Review of Gforth page. Since working with gforth, I've discovered that the ANSI Forth standard has a model for local variables in Forth. Since I've programmed in a number of other languages at least as much as I have with Forth, I found the local variables to be very helpful. I also found when doing some reading that a number of Forth versions have added some implementation of local variables at least a decade ago.
I ran some performance tests with gforth to see how the use of local variable performed compared with other methods of Forth coding. I used a simple test, the object of which was to pass 3 variables (call them a, b, and c) to a word, and have the word compute and return the sum of products ab+bc+ac. This was enough of a problem to make a bit of stack juggling necessary if no variables were used. The results from performing each method the same thousands of times are shown in the following table:
|Solution Method||Solution Time
|Using Stack Operations||9 seconds
|Using Global Variables||15 seconds
|Using Global Values||19 seconds
|Using Local Values||19 seconds
The reference in the table to global variables refers to the classic Forth variables that one must fetch and store. The Global values refers to the Forth values that use the => operator to replace the value. As you can see, while there is a bit of overhead to using local variables vs using only stack operations, it isn't terribly costly, and is about the same cost as using Global variables.
I used gforth to write a program that loads star catalogs. The program lets me select objects to view, and tells me where the point my telescope in order to view the objects. The program has about 50 words or routines in it, and I found that only two of the routines were clumsy enough that local variables helped keep them readable and maintainable. Putting that into perspective, it suggests that even though there is a small time cost in using local variables vs doing stack gymnastics, a typical programmer will probably only need or use local variables in a small percentage of routines, making the tradeoff of code readability vs efficiency a very tolerable tradeoff.
To thumb one's nose completely at the use of local variables seems to suggest to me that developers of other languages like c, perl, scheme, or whatever, have had no good ideas whatsoever, since they all make great use of local variables. I don't find that an acceptable point of view, and have found something quite clever about nearly every computer language I've ever studied.
So my view is that one should consider using local variables anytime that stack operations in a word definition become unwieldy. If factoring a complex word down into a few simpler words is relatively obvious, then factoring is probably a better approach, and will likely create faster code. But at least for me, sometimes I find too much time disappearing while I fumble with factoring. In those instances, using a few local variables lets me quickly get back to working on the rest of my program. And as my star catalog program suggests, generally only a few words in each program seem well suited for local variable use.
So I say -- why not.
Return To Index
Title: Speedup Programming With Local Variables
Of late, I've been using a modified form of a Forth coding technique that I learned long ago when using Forth on microcomputer projects. In that time honored approach, I would write an application entirely in high-level Forth code so that I could minimize development time. Then I would examine the program and identify words that were frequently referenced, such as within loops. These words were then coded in Forth assembly code to make them more efficient. With this approach, a very fast running program could be created even though only a few words were actually written in Forth assembly. The procedure worked well because words called often within loops do perhaps 90% of the actual work. Just optimizing these few words could make a huge difference in execution speed of a program.
Most of my programming nowadays is not on microcomputer projects, but on creating utilities for my desktop. I still write many of these utilities in Forth for a couple of reasons. One, I enjoy coding in Forth. Two, compared to most scripting languages, the Forth engine is very small, about 1/10 the size of perl, for example. So Forth produces fast running code compared to most scripting languages, and loads very quickly, making it a nice utility scripting language.
Since I don't work much with microprocessors at the current time, I don't delve into the Forth assembly language very often. But I do find the assembly optimization concept still useful. In my application of the old optimization approach, I create my first draft by making copious use of local variables, using them whenever a stack manipulation sequence or factoring procedure isn't obvious. This lets me create readable, functional code quickly.
Then, I examine the code and identify words that are called frequently. I then work to factor these words so that I can eliminate the use of local variables. In this way, only high level (infrequently called) words carry the overhead of local variables. By only doing the extra work on a handful of frequently called words, I keep the programming time manageable. The final result is code that is created quickly, and code that is readable, maintainable, and runs fast.
Return To Index