Next: , Previous: Literal Tutorial, Up: Tutorial


3.35 Advanced macros

Reconsider map-array from Execution Tokens. It frequently performs execute, a relatively expensive operation in some Forth implementations. You can use compile, and POSTPONE to eliminate these executes and produce a word that contains the word to be performed directly:

     : compile-map-array ( compilation: xt -- ; run-time: ... addr u -- ... )
     \ at run-time, execute xt ( ... x -- ... ) for each element of the
     \ array beginning at addr and containing u elements
       { xt }
       POSTPONE cells POSTPONE over POSTPONE + POSTPONE swap POSTPONE ?do
         POSTPONE i POSTPONE @ xt compile,
       1 cells POSTPONE literal POSTPONE +loop ;
     
     : sum-array ( addr u -- n )
      0 rot rot [ ' + compile-map-array ] ;
     see sum-array
     a 5 sum-array .

You can use the full power of Forth for generating the code; here's an example where the code is generated in a loop:

     : compile-vmul-step ( compilation: n --; run-time: n1 addr1 -- n2 addr2 )
     \ n2=n1+(addr1)*n, addr2=addr1+cell
       POSTPONE tuck POSTPONE @
       POSTPONE literal POSTPONE * POSTPONE +
       POSTPONE swap POSTPONE cell+ ;
     
     : compile-vmul ( compilation: addr1 u -- ; run-time: addr2 -- n )
     \ n=v1*v2 (inner product), where the v_i are represented as addr_i u
       0 postpone literal postpone swap
       [ ' compile-vmul-step compile-map-array ]
       postpone drop ;
     see compile-vmul
     
     : a-vmul ( addr -- n )
     \ n=a*v, where v is a vector that's as long as a and starts at addr
      [ a 5 compile-vmul ] ;
     see a-vmul
     a a-vmul .

This example uses compile-map-array to show off, but you could also use map-array instead (try it now!).

You can use this technique for efficient multiplication of large matrices. In matrix multiplication, you multiply every line of one matrix with every column of the other matrix. You can generate the code for one line once, and use it for every column. The only downside of this technique is that it is cumbersome to recover the memory consumed by the generated code when you are done (and in more complicated cases it is not possible portably).