Jak zmutovat prvky v car a cdr v cons? ====================================== - intuitivni reseni set!, ale to nefunguje, je to specialni forma, ktera vyzaduje na vstupu identifikator promenne, ne libovolne misto! - namisto toho budeme potrebovat set-car!, set-cdr! (nemusi byt nutne specialni forma) - POZOR!: v novem drracketu bohuzel nejsou - bližší info http://blog.racket-lang.org/2007/11/getting-rid-of-set-car-and-set-cdr.html - RESENI: stary drracket, MIT scheme,... - RESENI2: pouziti mcons, mcar, mcdr, set-mcar!, set-mcdr!, mlist, ... (je to kvuli lepsi optimalizaci) PR: (define a (cons 1 2)) (set-car a 3) a => ? (set-cdr! a 4) a => ? PR: (define a (cons 1 (cons 2 (cons 3 '())))) (define b (cons (cons 1 2) a)) (length b) => ? (set-car! a 4) a => ? b => ? PR: (define a (list 1 2 3)) (set-cdr! (cdr a)) a => ? PR: (predavani navratove hodnoty argumentem) 1. varianta ============ (let ((arg (cons 1 3))) ((lambda (a) (set-car! a (* (car a) 2)) (set-cdr! a (* (cdr a) 3))) arg) arg) => ? 2.varianta - mboxy ================== (define make-box (lambda (value) (lambda (signal . new-value) (cond ((equal? signal 'get) value) ((equal? signal 'set) (set! value (car new-value))) (else "neznamy signal"))))) (define proc (lambda (box n) (letrec ((f (lambda (n) (if (= n 1) 1 (* n (f (- n 1))))))) (box 'set (f n)) 'hotovo))) PR: napiste proceduru shorten-n, ktera destruktivne zkrati seznam na prvnich n prvku a vrati hodnotu (define a (list 1 2 3 4 5 6)) (shorten-n a 3) => '(1 2 3) a => '(1 2 3) (define shorten-n (lambda (l n) (if (<= n 1) (set-cdr! l '() (begin (shorten-n l (- n 1))) l)))) PR: Cyklicke seznamy - diky tomu ze pary jsou vlastne reference na dvojice, lze vyrabet cyklicke seznamy (v nektery cons nekonci '(), ale ukazuje na prvni prvek seznamu (muze byt i slozitejsi)) (define a (cons 'hello '())) (set-cdr! a a) a => ? PR: Vymodelujte graf pomoci paru graf, hrany grafu jsou definovany takto: {, , , , } Problem?: maximalne jeden naslednik (jak by se to dalo vyresit?) ===== 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 - 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)))) PROC to tak je? Odpoved jsou optimalizace. Reseni (otrocke) je definovat procedury znovu v prostredi makroexpanzni procedury. Dusledkem je hodne nepohodlna prace se seznamy = kodem. (define a 0) (define-macro foobar (lambda () `(+ 1 ,a))) (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)))) DEBUGGING MAKER: ================= - vypsani makroexpanze expanze (define-macro (lambda (argumenty) ... `'(expansion))) PRIKLAD: -------- (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) => (apply + '(0 1 2)) - 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 ;;;;; makra if* and a or - pripomenuti - ukazat anaforicka makra if, cond, ... POZN: anafora - neco co se v basni opakuje Anafora – Zlatý kolovrat (Kde's má Dorničko! Kde jsi? Kde Jsi? Kde's má roztomilá?) (define-macro if* (lambda (test expr . alt) `(let (($result ,test)) (if $result ,expr ,@alt)))) ;(if* 1 2 3) ;(if* 1 $result 3) (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) (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) ;;;; fluid-and (define-macro and* (lambda args (if (null? args) #t (if (null? (cdr args)) (car args) `(let (($result ,(car args))) (if $result (and* ,@(cdr args)) #f)))))) ;(and*) ;(and* 1 (= $result 1) 3 $result) ;(and* 1 #f 3) ;;;; pripomenuti cond (define-macro cond (lambda clist (if (null? clist) `(if #f #f) (if (equal? (caar clist) 'else) (cadar clist) `(if ,(caar clist) ,(cadar clist) (cond ,@(cdr clist))))))) ;;;; anaforicky cond - cond* (define-macro cond* (lambda clist (if (null? clist) `(if #f #f) (if (equal? (caar clist) 'else) (cadar clist) `(let (($result ,(caar clist))) (if $result ,(cadar clist) (cond* ,@(cdr clist)))))))) ;(cond* ((> 2 2) 2) ; ((equal? 2 2.) 3) ; (else 5)) ;(cond* ((> 2 2) 2) ; ((+ 2 2) $result) ; (else 5)) FLUID-LET: (define-macro my-fluid-let (lambda (xexe . body) (let ((xx (map car xexe)) (ee (map cadr xexe)) (old-xx (map (lambda (ig) (gensym)) xexe)) (result (gensym))) `(let ,(map (lambda (old-x x) `(,old-x ,x)) old-xx xx) ,@(map (lambda (x e) `(set! ,x ,e)) xx ee) (let ((,result (begin ,@body))) ,@(map (lambda (x old-x) `(set! ,x ,old-x)) xx old-xx) ,result))))) Makroexpanze: (my-fluid-let ((x 10)) (set! x 11)) => (let ((g12370 x)) (set! x 10) (let ((g12371 (begin (set! x 11)))) (set! x g12370) g12371)) PR: Symbol capture problem ====================== - Nekdy je ANAFORA nezadouci protoze prekryva promennou! Domaci Ukol: ============ 1) naprogramujte "objektove" slovnik, ktery akceptuje signaly 'get a 'set slovnik si pamatuje hodnoty ve tvaru klic -> hodnota (1b) Signal 'get bere jeden argument - klic do slovniku a vraci na klic navazanou hodnotu, pokud neni zadna vazba na klic vraci '() Signal 'set bere dva argumenty klic a hodnotu - na klic navaze hodnotu a vrati #t, pokud navic uzivatel nastavi jako hodnotu '() klic smazeme jako celek, pri implementaci pouzijte vedlejsi efekt pro modifikaci paru set-(m)car, set-(m)cdr,... Signal 'size vrati pocet klicu Priklad pouziti: (dictionary 'get 'hello) => () (dictionary 'size) => 0 (dictionary 'set 'hello 'world) => #t (dictionary 'get 'hello) => world (dictionary 'size) => 1 (dictionary 'set 'hello '()) => #t (dictionary 'size) => 0 2) 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 (1 bod) 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