A quotationt már tudod darabokból összerakni és lefuttatni. A quotation nem más, mint kódsorozat, ezért az ismert sorozatkezelő szavak is alkalmazhatók rá:
USING: kernel math prettyprint quotations sequences ;
{ [ 1 ] [ 2 + ] [ 3 * ] } concat >quotation call . ! => 9
[ 1 + ] dup append . ! => [ 1 + 1 + ]
Az append két quotationt fűz össze, a concat kilapítja a belőlük álló tömböt, a map adatokból készít quotationt, a >quotation pedig hívhatóvá teszi az eredményt. A MACRO: pontosan ezt a tudást veszi és viszi át fordítási időbe.
A MACRO: definíció úgy néz ki, mint egy közönséges szó, a törzse azonban aközben fut le, amikor a programodat fordítják, nem futás közben. A törzsnek egy quotationt kell a vermen hagynia. A fordító ezután befűzi azt a quotationt a hívás helyére: a makróhívást arra a kódra cseréli, amit a törzs előállított. Futásidőben már nem marad makró, csak a generált kód.
USING: kernel macros sequences ;
MACRO: twice ( quot -- spliced )
dup append ;
Bárhol leírod, hogy [ 1 + ] twice, a fordító a következőt teszi:
[ 1 + ] van a vermen,dup append felépíti a [ 1 + 1 + ] quotationt,1 + 1 +-t, arra a helyre.Így egy olyan szó, amely a twice-t hívja, úgy fordul le, mintha kézzel írtad volna le, hogy 1 + 1 +:
: inc-twice ( x -- y ) [ 1 + ] twice ;
5 inc-twice . ! => 7
A kulcsfontosságú különbség: egy közönséges szó, amely quotationt kap, futásidőben call-olja azt. A makró soha nem hív meg semmit: új kódot ad a fordítónak, hogy az a helyére fordítsa.
Mivel a törzs fordítási időben fut le, a makró bemeneteit már akkor ismerni kell. A hívás helyén literáloknak kell lenniük: egy [ … ] quotation, egy szám, egy tömb, egy string. Egy olyan értéket, amely csak futásidőben létezik, nem lehet átadni egy makrónak.
! DOCTEST: SKIP — illustrative call sites, not standalone code
[ 1 + ] twice ! ok: the quotation is a literal
[ 1 + ] 3 repeat-quot ! ok: the quotation and the number are literals
A makró törzse közönséges kód, amely egy quotationt ad vissza. A munka nagy részét olyan szavak végzik el, amelyekkel már találkoztál:
| mit építs… | mivel |
|---|---|
[ A B ] a [ A ]-ből és [ B ]-ből |
append |
| egy quotation a belőlük álló tömbből | concat >quotation |
| egy quotation a sorozat minden eleméből |
map, majd concat >quotation
|
A készletet két további szó egészíti ki, a sequences és a kernel szókészletből.
A <repetition> (a sequences szókészletben) egy elem n példányából álló sorozatot készít. A veremhatása ( length elt -- repetition ): a darabszám jön először. Az eredmény virtuális sorozat, ezért a szokásos módon lapítsd ki:
3 [ 1 + ] <repetition> concat >quotation . ! => [ 1 + 1 + 1 + ]
A curry (a kernel szókészletben) egy értéket fűz a quotation elé, és olyan új quotationt készít, amely először azt az értéket teszi a veremre:
3 [ + ] curry . ! => [ 3 + ]
{ 1 2 3 } [ . ] curry . ! => [ { 1 2 3 } . ]
A curry az, amivel egy konkrét elemet beégetsz egy quotationbe: pontosan ez alakítja a { 10 20 30 } tömböt és a [ + ] quotationt [ 10 + ] [ 20 + ] [ 30 + ]-vá.
Néhány nyelvben a makrókhoz különleges quote/unquote szintaxis és további gépezet kell a névütközések elkerüléséhez. A Factorban erre semmi sem kell: a quotation már feloldott szavak sorozata, mert a parser már akkor megkereste az egyes dup, + és append szavakat, amikor beolvasta a [ … ]-t. A makró törzse tehát nem más, mint a szokásos sorozatkezelés, amely épp egy quotationt ad vissza.
$[ … ]
Egy makró quotationt ad vissza, hogy befűződjön egy hívásba. Ha viszont egy egyszerű értéket szeretnél számítani elemzési időben, a $[ code ] lefuttatja a code-ot, amikor a fájlt olvassák, és az eredményt literálként beolvasztja a forrásba:
! DOCTEST: SKIP — compile-time value, shown for contrast
USING: calendar literals ;
CONSTANT: build-time $[ now ]
A $[ … ] a literals szókészletből származik, amely az elemzési idejű eredmények literálokba való beillesztéséhez tartozó szavakat gyűjti össze. A teljesség kedvéért említjük. A négy sablon, amelyet meg fogsz írni (twice, repeat-quot, compose-many és each-literal), mind MACRO: deklaráció.
Egy jelzőállomás kódgeneráló eszközkészletét gondozod. Minden makró egy sablon: egy kis minta, amellyel a fordító minden hívási helyen speciális kódot állít elő.
Te csak a négy makrót írod meg. A tesztfájl apró burkoló szavakat definiál, amelyek meghívják a makróidat (egy makrót csak a generált kódon keresztül lehet megfigyelni), majd ellenőrzi a burkoló szavak futásidejű eredményeit. A burkoló szavakat hagyd úgy, ahogy vannak.
Minden részfeladatnál a törzs a hívási hely literál bemeneteit kapja meg a vermen, és egy quotation maradjon utána. A »compiles to« sorok mutatják meg azt a lineáris kódot, amelyet a quotationnek elő kell állítania.
A twice egy literál quotationt kap, és olyan quotationt ad
vissza, amely az eredetit kétszer egymás után futtatja.
! : inc-twice ( x -- y ) [ 1 + ] twice ;
! body receives [ 1 + ] -> must build [ 1 + 1 + ]
5 inc-twice . ! => 7
A repeat-quot egy literál quotationt és egy literál n számot
kap, és olyan quotationt ad vissza, amely az eredetit n-szer
futtatja egymás után. n = 0 esetén az üres quotationt adja
vissza: [ ].
! : inc-thrice ( x -- y ) [ 1 + ] 3 repeat-quot ;
! body receives [ 1 + ] 3 -> must build [ 1 + 1 + 1 + ]
5 inc-thrice . ! => 8
A compose-many egy literál tömböt kap, amely quotationokat
tartalmaz, és egyetlen quotationt ad vissza, amely sorban
lefuttatja mindegyiket. Üres tömb esetén az üres quotationt adja
vissza.
! : pipeline ( x -- y ) { [ 1 + ] [ 2 * ] [ 1 - ] } compose-many ;
! body receives { [ 1 + ] [ 2 * ] [ 1 - ] } -> must build [ 1 + 2 * 1 - ]
5 pipeline . ! => 11
Az each-literal egy literál sorozatot és egy quotationt kap.
Olyan quotationt ad vissza, amely a sorozat minden eleménél a
veremre teszi az adott elemet, majd lefuttatja a quotationt.
Az iteráció így fordítási időben zajlik le, ezért a generált kód
lineáris.
! : sum-three ( -- n ) 0 { 10 20 30 } [ + ] each-literal ;
! body receives { 10 20 30 } [ + ] -> must build [ 10 + 20 + 30 + ]
sum-three . ! => 60
Ez a négy közül a leginkább szerkezeti jellegű: a generált kódot egyszerre alakítja a sorozat hossza és az elemeinek értékei. A tippek lépésről lépésre végigvezetnek az összeállításán.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Factor nyelvet 47 fogalom163 feladat segítségével, valódi emberi mentorálással, mindez ingyen.