KATEDR� INFORMATIKY� PÍRODOV DECK� FAKULT� UNIVERZIT� PALACKÉHO� OLOMOU�
PARADIGMAT� PROGRAMOVÁN� 2� MAKR� �
Slajd� vytvo°il� Vilé� Vychodi� � Ja� Kone£n�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � � � 3�
Opakování (kvazikvotování)
'(1 2 3 4 5) Z=) (1 2 3 4 5)
'(1 (+ 2 3) 4 5) Z=) (1 (+ 2 3) 4 5)
'(1 ,(+ 2 3) 4 5) Z=) (1 5 4 5)
'(1 1)) 4 5) Z=) (1 2) 4 5)
(define s '(a b c))
'(1 ,s 2) Z=) (1 (a b c) 2)
'(1 ,@s 2) Z=) (1 a b c 2)
'(1 '2 3) Z=) (1 (quote 2) 3)
(1 2 3) Z=) (quote (1 2 3))
(1 2 3) Z=) (quasiquote (1 2 3))
'(1 '(2 3)) Z=) (1 (quasiquote (2 3)))
'(1 '(,(+ 1 2) 3)) Z=) (1 (quasiquote 3) 3)))
'(1 ,'(,(+ 1 2) 3)) Z=) (1 (3 3))
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 2 / 35
Problém: chceme upravit if tak,
aby p°i absenci alternativního výrazu vracel #f
nyní máme:
(if (= 1 2) 'blah) Z=) nedenovaná hodnota
chceme:
(new-if (= 1 2) 'blah) Z=) #f
nabízí se vy°e²it pomocí nové procedury:
(define new-if
(lambda (elem1 elem2)
(if elem1 elem2 #f)))
p°i volání new-if je vºdy vyhodnocen i druhý argument:
(new-if #f blah-blah) Z=) Error
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 3 / 35
Pot°ebujeme�
b¥he�
vyhodnocován�
kaºd�
výra�
tvar�
(new-i� expr� expr2�
nahradi� výrazem�
(i� expr� expr� #f�
be� toh� ani� b� s� vyhodnocoval� expr� � expr�
Jiným� slovy�
pot°ebujem� zavés� p°edpis� kter� bud� provád¥� transformac� kódu� transformac� � � � konkrétn� £ás� kód� j� nahrazen� jino� p� transformac� prob¥hn� vyhodnocen� transformovanéh� kód� (defin� � 11� (new-i� (even� x� (� � 1)�
� transformac�
(i� (even� x� (� � 1� #f�
� vyhodnocen�
#�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � � � 3�
Jak se dívat na transformaci? m·ºeme si ji p°edstavit jako (klasickou) proceduru, které jsou p°edány argumenty v nevyhodnocené podob¥ transformace se v n¥kterých jazycích nazývá makroexpanze ;; transforma£ní procedura pro new-if (define new-if-trans (lambda (test expr . alt) (list 'if test expr (if (null? alt) #f (car alt))))) (new-if-trans 'e1 'e2 'e3) Z=) (if e1 e2 e3) (new-if-trans 'e1 'e2) Z=) (if e1 e2 #f) (new-if-trans '(even? x) '(+ x 1)) Z=) (if (even? x) (+ x 1) #f) (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 5 / 35 krat²í °e²ení pomocí kvazikvotování (define new-if-trans (lambda (test expr . alt) '(if ,test ,expr (begin #f ,@alt)))) p°íklady transformace: (new-if-trans 'expr1 'expr2 'expr3) Z=) (if expr1 expr2 (begin #f expr3)) (new-if-trans 'expr1 'expr2) Z=) (if expr1 expr2 (begin #f)) (new-if-trans '(even? x) '(+ x 1)) Z=) (if (even? x) (+ x 1) (begin #f)) (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 6 / 35 nouzov� °e²en� new-if� kter� s� ji� chov� ja� m� manuáln� spu²t¥n� transforma£n� procedur� následn� vyhodnocen� transformovanéh� výraz�
(eva� (new-if-tran� '(even� x� '(� � 1))�
Výhod� °e²ení�
poku� j� prvn� výra� nepravdivý� alternativn� výra� nen� vyhodnoce� tout� konstrukc� (new-if� lz� zastavi� rekurz� Nevýhod� °e²ení�
v²echn� p°edávan� argument� musím� explicitn� kvotova� transformovan� výra� musím� ru£n� vyhodnoti� pomoc� eva� eva� v� v¥t²in� interpret� pracuj� je� � globální� prost°ed� volán� j� nep°ehledn� (KI� U� Olomouc� P� 2A� Lekc� � Makr� � � � 3�
problém s lexikálními vazbami (let 4) (eval (new-if-transformer '(even? x) '(+ x 1)))) Z=) error: x not bound £áste£né °e²ení: pouºití (the-environment) ve v¥t²in¥ interpret· nebude fungovat (let 5) (eval (new-if-transformer '(even? y) '(+ y 1)) (the-environment))) navíc jsme se nezbavili nep°ehledného kódu e²ení problému: zavedení maker (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 8 / 35 MAKR� � dv� základn� pohled� n� makr� 1� POHLED� Makr� jso� roz²í°ení� syntax� jazyka� makr� � dán� denic� svéh� transforma£níh� p°edpis�
p� na£ten� výraz� (READ� j� � n¥� proveden� makroexpanz� tut� fáz� provád� tzv� preproceso� a� p� dokon£en� expanz� v²ec� make� nastáv� vyhodnocován� výraz� nem� smys� uvaºova� poje� aplikac� makra� takt� n� makr� pohlíº� v¥t²in� PJ� C� DrScheme� Commo� LISP,� � � Výhod� p°ístupu� preproceso� � vlastn� eva� jso� zcel� nezávisl�
preproceso� m·º� bý� aktivová� okamºit� p� na£ten� výraz� umoº¬uj� snadno� kompilac� kód� (� kompilované� kód� ji� pochopiteln� ºádn� makr� nejsou� Nevýhod� p°ístupu� makr� jso� mim� jazyk� (£ast� s� zapisuj� odli²n¥� t°eb� � C�
makr� nejso� element� prvníh� °ád� (KI� U� Olomouc� P� 2A� Lekc� � Makr� � � � 3�
MAKR� � dv� základn� pohled� n� makr� 2� POHLED� Makr� jso� speciáln� element� jazyka� makr� � elemen� jazyk� obsahujíc� ukazate� n� transf� procedur�
transforma£n� procedur� � � � klasick� procedur� j� pot°eb� roz²í°i� eval� p°ípad� kd� s� prvn� prve� seznam� vyhodnot� n� makr� makr� jso� uºivatelsk� denovan� speciáln� formy� takt� n� makr� budem� pohlíºe� m� (dál� t°eb� PJ� M4� TEX� Výhod� p°ístupu� makr� jso� element� prvníh� °ád�
� makr� lz� pracova� jak� � daty� moho� dynamick� vznikat/zanika� z� b¥h� program� m·ºem� uvaºova� koncep� anonymníh� makra� Nevýhod� p°ístupu� � makroexpanz� docház� a� p°� £innost� eva�
praktick� znemoº¬uj� ú£inno� kompilac� kód� p°� neuváºené� pouºíván� make� komplikuj� lad¥n� program� (KI� U� Olomouc� P� 2A� Lekc� � Makr� � 1� � 3�
Motiva£n� p°íklad� denic� make�
(define-macr� new-i� (lambd� (<test� <expr� � <alt>� (lis� 'i� <test� <expr�
(i� (null� <alt>� #� (ca� <alt>))))�
;� new-if� pomoc� kvazikvotován�
(define-macr� new-i� (lambd� (<test� <expr� � <alt>�
'(i� ,<test� ,<expr� (begi� #� ,@<alt>)))�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 1� � 3�
P°íkla� pouºit� makr�
;� new-if� pomoc� kvazikvotován�
(define-macr� new-i� (lambd� (<test� <expr� � <alt>�
'(i� ,<test� ,<expr� (begi� #� ,@<alt>)))�
(le� 6)�
� aktivac� transforma£n� procedur� makr�
(i� (even� x� (� � 1� (begi� #f)�
� vyhodnocen� výraz� � prost°edí� kd� � m� vazb� 1�
1�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 1� � 3�
Roz²í°ení EVAL
Eval[E;P]:
(A) Pokud je E cislo, . . . jako obvykle
(B) Pokud je E symbol, . . . jako obvykle
(C) Pokud je E seznam tvaru (E1 E2 En), pak nejprve provedeme
vyhodnocení prvního prvku E1 v prost°edí P a výslednou hodnotu
ozna£íme F1, to jest F1 := Eval[E1;P]. Mohou nastat £ty°i situace:
(C.1) Pokud F1 je procedura, . . . jako obvykle
(C.2) Pokud F1 je speciální forma, . . . jako obvykle
(C.3) Pokud F1 je makro jehoº transforma£ní procedura je T, pak
1 F0 := Apply[T
;
E2; : : : ;
En]
(F0 je výsledkem aplikace transf. procedury na nevyhodnocené arg.)
2 Výsledek vyhodnocení F elementu E v prost°edí P je denován
F := Eval[F0
;P]
(F je výsledek vyhodnocení elementu F0 v prost°edí P).
(C.e) Pokud F1 není procedura, speciální forma, ani makro,
pak vyhodnocení kon£í chybou CHYBA: První prvek seznamu . . . .
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 13 / 35
Lad¥ní maker: základní princip
potla£íme vyhodnocení transformovaného kódu
vyuºívá dodate£né KVOTOVÁNÍ
(define-macro new-if
(lambda (test expr . alt)
(list 'quote
(list 'if test expr
(if (null? alt) #f (car alt))))))
(new-if #f blah-blah) Z=) (if #f blah-blah #f)
(define-macro new-if
(lambda (test expr . alt)
(if ,test ,expr (begin #f ,@alt))))
(new-if #f blah-blah) Z=) (if #f blah-blah (begin #f))
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 14 / 35
chceme vytvo°it and2 dvou argument· vracející #t nebo #f
chceme vytvo°it pouze s pomocí if
;; nedosta£ující °e²ení pomocí procedury:
(define and2
(lambda (elem1 elem2)
(if elem1
(if elem2
#t
#f)
#f)))
p°edchozí má váºný nedostatek:
(and2 #f blah-blah) Z=) error (chceme #f)
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 15 / 35
and�
s�
dv¥m�
argument�
vracejíc�
konjunkc�
pot°ebujeme�
b¥he�
vyhodnocován�
kaºd�
výra�
(and�
expr�
expr2�
nahradi�
výraze�
(i�
expr�
(i�
expr�
#�
#f�
#f�
be�
toh�
ani�
b�
s�
vyhodnocoval�
expr�
�
expr�
e²en�
pomoc�
makra�
(define-macr�
and�
(lambd�
(expr�
expr2�
'(i�
,expr�
(i�
,expr�
#�
#f�
#f))�
(KI�
U�
Olomouc�
P�
2A�
Lekc�
�
Makr�
�
1�
�
3�
Ukázky pouºití and2
(and2 1 (+ 1 2))
+
(if 1 (if (+ 1 2) #t #f) #f)
+
#t
(and2 #f blah-blah)
+
(if #f (if blah-blah #t #f) #f)
+
#f
(and2 #t #f)
+
(if #t (if #f #t #f) #f)
Z=) #f
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 17 / 35
Anaforický if: if*
if*, který pracuje stejn¥ jako if, ale umoº¬uje v druhém a t°etím
výrazu pouºívat symbol $result, který bude vºdy navázaný na
výsledek vyhodnocení prvního výrazu
praktické roz²í°ení, místo:
(if (member 'b '(a b c d))
(list 'nalezen (member 'b '(a b c d)))
'blah)
sta£í napsat:
(if* (member 'b '(a b c d))
(list 'nalezen $result)
'blah) Z=) (nalezen (b c d))
(KI, UP Olomouc) PP 2A, Lekce 3 Makra I 18 / 35
e²ení�
b¥he�
vyhodnocován�
kaºd�
výra�
(if�
expr�
expr�
expr3�
pot°ebujem�
nahradi�
výrazem�
(le�
7))�
(KI�
U�
Olomouc�
P�
2A�
Lekc�
�
Makr�
�
1�
�
3�
Ukázk�
pouºit�
if�
be�
$resul�
s�
chov�
jak�
normáln�
i�
(if�
�
�
3�
�
(le�
8)))�
(i�
$resul�
(lis�
(quot�
nalezen�
$result�
(quot�
blah))�
�
(naleze�
(�
�
d)�
V²imn¥t�
si�
�
expandované�
výraz�
nejso�
ºádn�
�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
i� pomoc� con� (define-macr� i�
(lambd� (tes� exp� alt�
'(con� (,tes� ,expr�
(els� ,alt)))�
i� pomoc� con� (be� nutnost� mí� alternativn� v¥tev�
(define-macr� i� (lambd� (tes� exp� � alt� '(con� (,tes� ,expr� (els� (begi� #� ,@alt))))�
podobn� jak� p°edchozí� al� vracím� nedenovano� hodnot�
(define-macr� i� (lambd� (tes� exp� � alt� '(con� (,tes� ,expr� (els� (begi� (cond� ,@alt))))�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
con� pomoc� i� musím� p°epsa� jede� cond-výra� pomoc� n¥kolik� if�
;� základn� con� pomoc� i� (pomoc� rekurzivníh� vno°ení�
(define-macr� con�
(lambd� clis�
(le� dive-if� 9))))))�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
P°íkla� pouºit�
(con� 10)� �
(i� (� � 3� (quot� blah� (i� (� � 10� (� � x� (i� (prop� � y� (lis� � y� (� 20)))� � �
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
con� pomoc� i� musím� p°epsa� jede� cond-výra� pomoc� n¥kolik� if�
;� základn� con� pomoc� i� (°e²en� jak� rekurzivn� makro�
(define-macr� con�
(lambd� clis�
(i� (null� clist�
'(i� #� #f�
(i� (equal� (caa� clist� 'else�
(cada� clist�
'(i� ,(caa� clist�
,(cada� clist�
(con� ,@(cd� clist))))))�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
P°íkla� pouºit�
(con� 11)�
�
(i� (� � 3� (quot� blah� (con� 12))�
� �
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
Roz²í°en� verz� defin� defin� jsm� zatí� pouºíval� pouz� v� tvar�
(defin� symbo� /vyra� .�
� R6R� Schem� j� defin� zavede� tak� v� tvar�
(defin� (symbo� /argument� ….� /vyraz� ….�
P°íklad� faktoriá�
(defin� (� n�
(i� (� � 1�
�
(� � (� (� � 1))))�
P°íklad� nepovinn� argument�
(defin� (� � � � args� (lis� � � args)�
(KI� U� Olomouc� P� 2A� Lekc� � Makr� � 2� � 3�
Roz²í°ená verze define Pokud by ná² interpret nedisponoval roz²í°eným define, pak bychom jej mohli vyrobit jako makro: (define-macro def (lambda (first . args) (if (symbol? first) '(define ,first ,@args) '(define ,(car first) (lambda ,(cdr first) ,@args))))) P°íklad pouºití: (def (f n) (if (= n 1) 1 (* n (f (- n 1))))) (f 6) Z=) 720 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 28 / 35 Konjunkce libovoln¥ mnoha argument· pomocí if ;; základní verze (define-macro and (lambda args (if (null? args) #t '(if ,(car args) (and ,@(cdr args)) #f)))) (and 1 2 3) Z=) #t protoºe: (and 1 2 3) Z=) (if 1 (and 2 3) #f) Z=) (and 2 3) Z=) (if 2 (and 3) #f) Z=) (and 3) Z=) (if 3 (and) #f) Z=) (and) Z=) #t Z=) #t (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 29 / 35 Konjunkce libovoln¥ mnoha argument· pomocí if ;; zlep²ená verze (zobecn¥né pravdivostní hodnoty) (define-macro and (lambda args (if (null? args) #t (if (null? (cdr args)) (car args) '(if ,(car args) (and ,@(cdr args)) #f))))) nyní se jiº chová jako klasický and: (and) Z=) #t (and 1 2 3) Z=) 3 (and 1 #f 3) Z=) #f (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 30 / 35 Disjunkce libovoln¥ mnoha argument· pomocí if ;; základní verze (define-macro or (lambda args (if (null? args) #f '(if ,(car args) #t (or ,@(cdr args)))))) P°íklad pouºití: (or) Z=) #f (or 1 2 3) Z=) #t cht¥li bychom 1 (or #f 2 3) Z=) #t cht¥li bychom 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 31 / 35 Disjunkce libovoln¥ mnoha argument· pomocí if ;; roz²í°ená verze (define-macro or (lambda args (if (null? args) #f (if (null? (cdr args)) (car args) '(if ,(car args) ,(car args) (or ,@(cdr args))))))) Chová se (zdánliv¥) v po°ádku: (or) Z=) #f (or 1 2 3) Z=) 1 (or #f 2 3) Z=) 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 32 / 35 Problémy s implementací disjunkce pomocí if na²e implementace or: dvakrát vyhodnocuje pravdivé argumenty (krom¥ posledního) d·sledek: nechová se jako klasický or pokud pouºijeme vedlej²í efekt Chování klasického or (let 13) (or (begin (set! x (+ x 1)) x) blah)) Z=) 1 Chování na²eho or: (let 14) (or (begin (set! x (+ x 1)) x) blah)) Z=) 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 33 / 35 Problémy s implementací disjunkce pomocí if ;; pokus o °e²ení p°edchozího problému (define-macro or (lambda args (if (null? args) #f (if (null? (cdr args)) (car args) '(let 15)) (if result result (or ,@(cdr args)))))))) Nyní uº jsme p°edchozí problém odstranili, . . . (let 16) (or (begin (set! x (+ x 1)) x) blah)) Z=) 1 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 34 / 35 . . . ale nový problém jsme vyrobili Klasický or: (let 17) (or #f result)) Z=) 10 Ná² or: (let 18) (or #f result)) + (let 19) (if result result (or result))) Z=) #f do²lo k p°ekrytí symbolu result symbolem stejného jména, který je pouºíván uvnit° na²eho makra tomuto efektu se °íká symbol capture (variable capture) v dal²í lekci ukáºeme £isté °e²ení tohoto problému (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 35 / 35