Application: Word Usage Counts
When you want to optimize the performance of a program, it is a common practice to find the most frequently used words and make them top priority. Hence, here we are writing a program to record the counts of all the self-defined words being used without redefining them.
Step 1
Create a hash called "counts" which stores the identities of the words to be defined and their respective number of counts.
# constant counts
After the hash table is created, what do you think the key should be?
Step 2
Redefine the word;so that when a new word completes its compilation, it will initialize the counts value to be 0 for this word.
: ; ` ; latestxt 0 counts #! ; immediate
latestxtwill refer to the latest XT encountered. Therefore, when a new word is being defined,
latestXTis the XT of that word.
Step 3
Redefine the word:so that when your word runs, it will increment its value in the hash table "counts" by 1. Before implementing this function, don't forget that since the word
;has been redefined, it will create an entry in the table "counts" for each word you define in this step. Therefore, you need to use
ALIASto backup the original definition of the word
;into a new word.
: this ( -- xt ) latestxt ` literal ; immediate : counts@ counts #@ ; : counts! ( n -- ) counts #! ; : +counts ( xt -- ) dup counts@ 1+ counts! ; ' ; alias ;; \ create the word ;; which has the original function of ; : ; ` ; latestxt 0 counts #! ; immediate : : : ` this ['] +counts compile, ;;
When a word consists of many actions, remember to break it into several parts to define some more words of these actions so that it will look simpler and more comprehensible to human
Step 4
Define a word.COUNTSto display the counts of all the words. Recall that
#KEYSreturns a sequence of all the keys in a hash and we need to use a loop to display the count value associated with each XT. First of all, we need to write a word
REDUCEwhich requires a sequence and an XT. This word will execute the provided XT on each element of the sequence.
: reduce ( seq xt -- ) ~ { f } begin dup empty? -> drop exit |. dup head f tail again ;Next, we can use
REDUCEto write a word
.COUNTSwhich displays all the count value of each XT.
: .counts counts #keys [: { k } k name? . "-->" . k counts #@ . cr \ NAME? gets the word's name from XT ;] reduce ;Make sure that the definitions in Step 4 are placed before words
:and
;so that the issue mentioned in Step 3 can be avoided.
Quiz
Question 1
In step 3, we redefine the word:instead of
;. Why do we increment the count at the beginning of running a word instead of the end? Hint: under which circumstances a word cannot reach its end?
exitcommand.
Question 2
In step 3, we usedTHIS(you have come across this word in the earlier tutorial. Can you recall what it does?). What will happen if we just conveniently compile
LATESTXTinto each word?
LATESTXTas a literal is required to be used as the key for
+COUNTs
Question 3
Other than the frequency, the total duration of your words running in the program is also a key factor when you want to determine the performance. Hence, modify the example so that it records the total duration.
# constant counts : this ( -- xt ) latestxt ` literal ; immediate : counts@ counts #@ ; : counts! ( xt n -- ) counts #! ; : -counts tuck counts@ swap - counts! ; : +counts ( n xt -- ) tuck counts@ + counts! ; : exit ` now ` this ['] +counts compile, ` exit ; immediate ' ; alias ;; \ create the word ;; which has the original function of ; : ; ` now ` this ['] +counts compile, ` ; latestxt 0 counts #! ; immediate : : : ` now ` this ['] -counts compile, ;; : .counts counts #keys [: { k } k name? . "-->" . k counts #@ . cr ;] reduce ;