Makra 1
Opakování (kvazikvotování)
'(1 2 3 4 5) ;=> (1 2 3 4 5) '(1 (+ 2 3) 4 5) ;=> (1 (+ 2 3) 4 5) '(1 ,(+ 2 3) 4 5) ;=> (1 5 4 5) '(1 ((,(+ 2 3))) 4 5) ;=> (1 ((5)) 4 5) (define s '(a b c)) '(1 ,s 2) ;=> (1 (a b c) 2) '(1 ,@s 2) ;=> (1 a b c 2) '(1 '2 3) ;=> (1 (quote 2) 3) ''(1 2 3) ;=> (quote (1 2 3)) ''(1 2 3) ;=> (quasiquote (1 2 3)) '(1 '(2 3)) ;=> (1 (quasiquote (2 3))) '(1 '(,(+ 1 2) 3)) ;=> (1 (quasiquote ((unquote (+ 1 2)) 3))) '(1 ,'(,(+ 1 2) 3)) ;=> (1 (3 3))
Problém: chceme upravit if tak, aby při absenci alternativního výrazu vracel #f nyní máme: (if (= 1 2) 'blah) ;⇒ nedefiovaná hodnota chceme: (new-if (= 1 2) 'blah) ;⇒ #f nabízí se vyřesit 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) ;⇒ Error
Potřebujeme během vyhodnocování každý výraz tvaru
(new-if expr1 expr2)
nahradit výrazem
(if expr1 expr2 #f
bez toho aniž by se vyhodnocovalo expr
expr
Jinými slovy potřebujeme zavést předpis, ktery bude provádět transformaci kódu transformaci konkrétni část 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
#
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) ;⇒ (if e1 e2 e3) (new-if-trans 'e1 'e2) ;⇒ (if e1 e2 #f) (new-if-trans '(even? x) '(+ x 1)) ;⇒ (if (even? x) (+ x 1) #f)
kratsí řesení 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) ;⇒ (if expr1 expr2 (begin #f expr3)) (new-if-trans 'expr1 'expr2) ;⇒ (if expr1 expr2 (begin #f)) (new-if-trans '(even? x) '(+ x 1)) ;⇒ (if (even? x) (+ x 1) (begin #f))
nouzové řesení new-if které se již chová jak má
manuáln spustěn transformačn procedur následn vyhodnocen transformovanéh výraz
(eval (new-if-trans '(even? x '(
1))
Výhod řesení
pokud je první výraz nepravdivý alternativní výraz není vyhodnocen touto konstrukcí (new-if lz zastavi rekurz Nevýhod řesení
problém s lexikálními vazbami
(let ((x 10)) (eval (new-if-transformer '(even? x) '(+ x 1)))) ;=> error: x not bound
částečné řesení: použití (the-environment) ve větsině interpretů nebude fungovat
(let ((y 10)) (eval (new-if-transformer '(even? y) '(+ y 1)) (the-environment)))
navíc jsme se nezbavili nepřehledného kódu Řesení problému: zavedení maker
MAKRA dva základní pohledy na makra 1 POHLED Makra jsou rozsířením syntaxe jazyka
makro
dáno 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 vsec make nastáv vyhodnocován výraz nem smys uvažova poje aplikac makra takt n makr pohlíž větsin 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 odlisně třeb
C
makr nejso element prvníh řád
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 rozsíř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ýhody přístupu makroexpanze dochází a př činnost eva
praktick znemož¬uj účinno kompilac kód př neuvážené používán make komplikuj laděn program
AMotivační příklad defiice 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 1)
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
Rozsíř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) ;⇒ (if #f blah-blah #f)
(define-macro new-if
(lambda (test expr . alt)
(if ,test ,expr (begin #f ,@alt))))
(new-if #f blah-blah) ;⇒ (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í řesení pomocí procedury:
(define and2
(lambda (elem1 elem2)
(if elem1
(if elem2
#t
#f)
#f)))
předchozí má vážný nedostatek:
(and2 #f blah-blah) ;⇒ 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
esen
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)
;⇒ #f
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é rozsíř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) ;⇒ (nalezen (b c d))
Řešení: během vyhodnocování každý výraz
(if expr1 expr2 expr3)
potřebujeme nahradit výrazem:
(let
2))
t
opě
be
vyhodnocován
expr
a
expr
if
jak
makr
(define-macr
if
(lambd
(tes
exp
alt
'(le
3))
Ukázka použití
if bez $result se chová jako normální if
(if
3
(le
4)))
(i
$resul
(lis
(quot
nalezen
$result
(quot
blah))
(naleze
(
d)
Vsimněte si expandované výrazy
nejsou
žá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 5))))))
(KI U Olomouc P 2A Lekc
Makr
2
3
Příkla použit
(con 6)
(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 (řesen 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 7)
(i (
3 (quot blah (con 8))
(KI U Olomouc P 2A Lekc
Makr
2
3
Rozsíř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
Rozsířená verze define Pokud by nás interpret nedisponoval rozsíř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) ;⇒ 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) ;⇒ #t protože: (and 1 2 3) ;⇒ (if 1 (and 2 3) #f) Z=) (and 2 3) ;⇒ (if 2 (and 3) #f) Z=) (and 3) ;⇒ (if 3 (and) #f) Z=) (and) ;⇒ #t Z=) #t (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 29 / 35 Konjunkce libovolně mnoha argumentů pomocí if ;; zlepsená 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) ;⇒ #t (and 1 2 3) ;⇒ 3 (and 1 #f 3) ;⇒ #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) ;⇒ #f (or 1 2 3) ;⇒ #t chtěli bychom 1 (or #f 2 3) ;⇒ #t chtěli bychom 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 31 / 35 Disjunkce libovolně mnoha argumentů pomocí if ;; rozsíř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) ;⇒ #f (or 1 2 3) ;⇒ 1 (or #f 2 3) ;⇒ 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 32 / 35 Problémy s implementací disjunkce pomocí if nase implementace or: dvakrát vyhodnocuje pravdivé argumenty (kromě posledního) důsledek: nechová se jako klasický or pokud použijeme vedlejsí efekt Chování klasického or (let 9) (or (begin (set! x (+ x 1)) x) blah)) ;⇒ 1 Chování naseho or: (let 10) (or (begin (set! x (+ x 1)) x) blah)) ;⇒ 2 (KI, UP Olomouc) PP 2A, Lekce 3 Makra I 33 / 35 Problémy s implementací disjunkce pomocí if ;; pokus o řesení předchozího problému (define-macro or (lambda args (if (null? args) #f (if (null? (cdr args)) (car args) '(let 11)) (if result result (or ,@(cdr args)))))))) Nyní už jsme předchozí problém odstranili, . . . (let 12) (or (begin (set! x (+ x 1)) x) blah)) ;⇒ 1
. . . ale nový problém jsme vyrobili Klasický or: (let 13) (or #f result)) ;⇒ 10 Náš or: (let 14) (or #f result)) + (let 15) (if result result (or result))) ;⇒ #f doslo k překrytí symbolu result symbolem stejného jména, který je používán uvnitř naseho makra tomuto efektu se říká symbol capture (variable capture) v dalsí lekci ukážeme čisté řesení tohoto problému