Execution Tokens の map-array
について再検討してみましょう。 map-array は execute を頻繁に実行しますが、 これは一部の Forth
実装では比較的高価な操作です。 compile, と POSTPONE を使用すると、 これらの
execute を削除し、 直接実行されるワードを含むワードを生成できます:
: 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 .
コードの生成には Forth の機能を最大限に活用できます。 以下に、 コードがループ内で生成される例を示します:
: 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 .
この例では compile-map-array を使用していますが、 代わりに map-array
を使用することもできます(是非試してみてください)。
この手法を使用すると、 巨大な行列を効率的に乗算できます。 行列の乗算では、 一方の行列のすべての行ともう一方の行列のすべての列を乗算します。 1 行のコードを 1 回生成し、 それをすべての列に使用できます。 この手法の唯一の欠点は、 生成されたコードによって消費されたメモリーを完了時に開放するのが面倒なことです(さらに複雑な場合は移植可能ではありません)。