===== MAKRA ===== - zopakovat quasikvotovani - vyrabi seznam aniz by vyhodnocoval prvky `(1 2 3) => `(1 '2 3) => - vyhodnoceni prvku se da vynutit pomoci , `(1 (- 4 2) 2) => `(1 ,(- 4 2) 2) => `(1 '(1 2 3) 4) => `(1 ,'(1 2 3) 4) => - vnoreny seznam lze "rozbalit" pomoci ,@ `(1 ,@'(1 2 3) 4) => - PR: napiste proceduru let, ktera expanduje let vyraz na lambda vyraz (neuvazujeme pojmenovany let) (my-let-expansion '(let ((a 10)) (print a))) => ((lambda (a) (print a)) 10) (define my-let (lambda (let-expr) (let* ((vars (list-ref let-expr 1)) (symb (map (lambda (x) (car x)) vars)) (vals (map (lambda (x) (car (cdr x))) vars)) (body (cddr let-expr))) `((lambda ,symb ,@body) ,@vals)))) (eval (my-let '(let ((a 10)) (print a)))) => ? - makra - Makro ve schemeu je specialni forma, ktera transformuje program na jiny program (makroexpanze), vysledek makroexpanze se "prozene" evalem - je potreba rozsirit vyhodnocovaci proces - makra mohou byt rekurzivni (narozdil od vetsiny jazyku!) - makra jsou jako RUCNI granat - bacha rady vybuchuji v ruce:) - takze opatrne - ve schemeu nova specialni forma define-macro - syntaxe (define-macro symbol transformacni_procedura) - transformacni_procedura musi vracet validni program - protoze ten se vyhodnoti evalem - transformacni procedura dostava argumenty v nevyhodnocenem stavu - pouze jako kod = seznam Vyhodnocovaci proces makra: - Mějme makro definované (define-macro M transformation-lambda) - máme seznam tvaru (M p1 p2 ...), M je makro, pak vyhodnocujeme (eval (transformation-lambda 'p1 'p2 ...)) - POZOR: transformacni procedura makra nevznika v lexikalnim prostredi!, dokonce nema vazbu ani do globalniho prostredi (jen na jeho urcitou podmnozinu = builtin procedury - docela zasadni omezeni). Jinymi slovy, makra ziji ve svem vlastnim prostredi. Napr toto nefunguje!: (define a 0) (define-macro foobar (lambda () `(+ 1 ,a))) Dokonce ani toto: (define-macro foobar (lambda () (let ((body (build-list 3 (lambda (x) x)))) `(apply + ',body)))) DEBUGGING MAKER: ================= - vypsani makroexpanze expanze (define-macro (lambda (argumenty) ... `'(expansion))) PRIKLADY: -------- (define-macro foobar (lambda () (define build-list (lambda (n proc) (let iter ((c 0)) (if (= c n) null (cons (proc c) (iter (+ c 1))))))) (let ((body (build-list 3 (lambda (x) x)))) `'(apply + ',body)))) > (foobar) => ? * Napište makro imply, které se chová jako implikace a překládá se na (or (not a) b) * Napište makro (unless condition stmt) - ledaže neplatí condition provede se stmt Př: (unless #t (print 'here)) => #f (unless #t xZb) => #f (unless #f 5) => 5 * Napište makro rev-begin, které obrací pořadí vykonávání kódu Př: (rev-begin (print 'world) (print 'hello)) => helloworld * Zanalyzujte: (define-macro and (lambda args (if (null? args) #t (if (null? (cdr args)) (car args) `(if ,(car args) (and ,@(cdr args)) #f))))) ;(and) ;(and 1 2 3) ;(and 1 #f 3) * Zanalyzujte: (define-macro or (lambda args (if (null? args) #f (if (null? (cdr args)) (car args) `(let ((result ,(car args))) (if result result (or ,@(cdr args)))))))) ;(or) ;(or 1 2 3) ;(or #f 2 3) * Napište makro consequence, fungující jako vnořená implikace Např: (consequence a b c) spočítá (a => b) => c, pokud je jisté, že odpověd je #f další argumenty se nevyhodnocují (podobně jako and, or) Anaforická makra - makra pamatující si výsledek vyhodnocení, například podmínky, kterou zpřístupní uživatelskému kódu (napříkad by bylo pěkné v ifu znát výsledek vyhodnocení podmínky) - napiste vetveni podle znamenka cisla, znamenko bude ulozeno v $sign to se muze rovnat -1, 0, 1, vyhodnoti se jenom odpovidajici vetev! PR: (sign-if -128 (print 'hello) (segmenation fault) xD) => vytiskne hello (sign-if 128 (blah) (foobar) (print $sign)) => vytiskne 1 - POZN: lze takto definovat i let, let*, letrec, fluid-let ... viz slajdy Domaci Ukol - ukol na hodine: ============================= (1 bod - za oba) (1) napiste anaforicky imply, ktere se chova podobne jako if ale ne forme ekvivalence ale implikace, anafora se jmenuje $result a je navazana na vysledek vyhodnoceni podminky, v pripade, ze se maji vyhodnotit obe vetve nejdrive se vyhodnocuje true vetev a pak false PR: (imply #t (print 'true) (print 'false)) => undefined, vytiskne true (imply #f (print 'true) (print 'false)) => undefined, vytiskne true, vytiskne false (protoze je pravda 0 => 1 i 0 => 0) (imply (< (* 3 2) 1) (print 'here) 2) => 2 , vytiskne here ;; vraci vyhodnoceni false vetve ale provede se i true vetev (imply (* 3 2) (print $result) xD) => undefined, vytiskne 6 (define a 5) (imply (equal? 2 (set! a (+ a 15))) $result $result) => #f a => 20 (2) Napiste makro alambda, ktere se chova jako lambda, az na to ze na symbol self je navazana tato lambda - je mozna rekurze bez define (define factorial (alambda (x) (if (> x 0) (* x (self (- x 1))) 1))) (factorial 5) => 120