vedlejší efekt set! Syntaxe a Sémantika =================== (set! symbol value) - nastavení proměnné "natvrdo" podobně jako v C: int a; /* (define a '()) */ a = 10;/* (set! a 10) */ Sémantika, vyhledá standardně vazbu symbolu (pokud neexistuje, vrátí chybu) a přenastaví vazbu symbolu na novou hodnotu Př: (define a 10) a => 10 (set! a 100) a => 100 (let ((a 1000)) (set! a 8) (display a)) => 8 a => ? (set! a (+ a 1)) a => ? (define tst (lambda () (let ((a 0)) (set! a (+ a 1))))) (tst) a => ? (define tst (let ((a 0)) (lambda () (set! a (+ a 1))))) (tst) a => ? Fluidní let =========== - dočasně přepíše vazbu symbolu, po opuštění těla fluid-let se vrátí k původní vazbě POZN: fluidum: • domnělá neviditelná látka jako nositel fyzikálních vlastností • esprit, kouzlo osobnosti nebo místa PR: (define f 1024) (define half (lambda () (set! f (floor (/ f 2))) f)) (fluid-let ((f 8)) (half)) f => ? PR: (define call-count 0) ; pocita pocet zavolani (define inc-calls (lambda () (set! call-count (+ call-count 1)) call-count)) (define compute (lambda (x) (fluid-let ((call-count (* x 2))) (inc-calls) (sqrt call-count)))) (compute 14) => ? call-count => ? Procedura s vnitrnim stavem: ============================ (define inc (let ((inner-state 0)) (lambda (x) (set! inner-state (+ inner-state x)) inner-state))) (inc 10) => ? (inc 1) => ? ... Procedury sdilejici jeden vnitrni stav: ======================================= - podobne jako pred okamzikem, jenom je nutne, aby vsechny procedury vznikly v prostredi letu predstavujiciho vnitrni stavem - musime vymyslet, jak ty procedury vrátíme (například v seznamu, consu apod.) PR: (define inc-dec (let ((counter 0)) (cons (lambda (x) (set! counter (- counter x)) counter) (lambda (x) (set! counter (+ counter x)) counter)))) (define dec (car inc-dec)) (define inc (cdr inc-dec)) (inc 1) => (dec 2) => (inc 8) => "Objektovy" system: =================== Objekt je procedura s vnitrnim stavem. Vnitrni stav objektu modifikuji prichazejici signaly, ktere odbavujeme tzv. dispatcherem (odbavovacem) (define stack (let ((s '()) (len 0)) (define push (lambda (x) (set! s (cons x s)) (set! len (+ len 1)))) (define pop (lambda () (if (equal? s '()) '() (let ((out (car s))) (set! s (cdr s)) (set! len (- len 1)) out)))) (lambda (sig . args) (cond ((equal? sig 'push) (apply push args)) ((equal? sig 'pop) (apply pop args)) (else (error "no such signal")))))) 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) - pro práci s m-seznamy viz. https://docs.racket-lang.org/compatibility/mlists.html 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 (cdr 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?) Ukol na cviceni --------------- 1) Napiste schemeovskou verzi funkci rand a srand s vyuzitim vnitrniho stavu procedury. Rand slouzi ke generovani pseudonahodnych cisel. Srand k inicializaci stavu pocitadla. POZN: NEPOUZIVEJTE zadnou globalni promennou! (vyjimku tvori promenne, na ktere jsou navazane procedury) V jazyce C se rand a srand lze jednoduse naprogramovat takto: (man 3 rand) static unsigned long next = 1; int myrand(void) { next = next * 1103515245 + 12345; return((unsigned)(next/65536) % 32768); } void mysrand(unsigned seed) { next = seed; } Procedura srand musi vracet vzdy #t a nastavuje seed Procedura rand vraci pseudonahodne cislo TEST: (srand 0) => #t (rand) => 0 (rand) => 21468 (rand) => 9988 (srand 12)=> #t (rand) => 5451 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 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