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í

  • vsechný předávané argumentý musíme explicitně kvotovat
  • transformovaný výraz musíme ručně vyhodnotit pomocí eval
  • eval ve větsině interpretů pracuje jen v globálním prostředí
  • volání je nepřehledné

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

1)
10) (new-i (even x ( 1
2)
$resul expr1) (if $resul expr2 expr3
3)
$resul ,test) (i $resul ,exp ,@alt
4)
$resul 1) (i $resul 3) příklad použití $result (if $resul 3 (le (($resul 1) (i $resul $resul 3) Složitějsí ukázka použití if (if (membe ' '( d) (lis 'naleze $result 'blah (le (($resul (membe (quot b (quot ( d
5)
clis clist) (i (null clist '(i # #f (i (equal (caa clist 'else (cada clist '(i ,(caa clist ,(cada clist ,(dive-if (cd clist
6) , 7)
3 'blah (( 10 ( x) ((prop y (lis y) (els ( 20
8)
10 ( x) ((prop y (lis y) (els ( 20
9) , 10) , 12)
x 0
11)
result ,(car args
13) , 14)
result 10
15)
result #f
PAPR2/L3.txt · Last modified: 2014/04/24 00:53 (external edit)
CC Attribution-Noncommercial-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0