Ohjelmoinnin peruskurssien laaja oppimäärä

Samankaltaiset tiedostot
Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 3

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 2

Scheme-kesäkurssi luento 5

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Scheme-kesäkurssi luento 4

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Scheme-kesäkurssi luento 1

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Makrojen mystinen maailma lyhyt oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 5: Python

JReleaser Yksikkötestaus ja JUnit. Mikko Mäkelä

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssien laaja oppimäärä

TIEA341 Funktio-ohjelmointi 1, kevät 2008

815338A Ohjelmointikielten periaatteet

Tutoriaaliläsnäoloista

Ohjelmoinnin perusteet Y Python

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

Ohjelmoinnin peruskurssi Y1

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Scheme-kesäkurssi luento 6

Luento 5. Timo Savola. 28. huhtikuuta 2006

Harjoitustyö: virtuaalikone

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Racket ohjelmointia osa 2. Tiina Partanen Lielahden koulu 2014

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Ohjelmoinnin perusteet Y Python

Hohde Consulting 2004

Ohjelmoinnin perusteet, syksy 2006

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssi Y1

11/20: Konepelti auki

Ohjelmoinnin perusteet Y Python

Loppukurssin järjestelyt C:n edistyneet piirteet

Muistutus aikatauluista

Yhteydettömät kieliopit [Sipser luku 2.1]

Ohjelmoinnin peruskurssi Y1

811120P Diskreetit rakenteet

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Loppukurssin järjestelyt

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssi Y1

Clojure, funktionaalinen Lisp murre

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

Harjoitus 5 (viikko 48)

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssien laaja oppimäärä, kevät

Ohjelmoinnin perusteet Y Python

811120P Diskreetit rakenteet

Groovy. Samuli Haverinen, Aki Hänninen. 19. marraskuuta 2015

Ohjelmoinnin perusteet Y Python

15. Ohjelmoinnin tekniikkaa 15.1

Ohjelmoinnin perusteet Y Python

14. Poikkeukset 14.1

Ohjelmoinnin perusteet Y Python

Ruokahalu kasvaa syödessä lisää makrokielen herkkuja

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

Python-ohjelmointi Harjoitus 2

Transkriptio:

Ohjelmoinnin peruskurssien laaja oppimäärä Luento 10: Tulkin muokkaus, makrot, ohjelmia muokkaavat ohjelmat (mm. SICP 3.2.4, 4-4.1.6) Riku Saikkonen 22. 11. 2011

Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta: sisäiset määrittelyt (SICP 3.2.4 ja 4.1.6) 3 Makrot 4 Ohjelmia muokkaavat ohjelmat 5 Kierroksen 3 tehtävien ratkaisut

Tulkin muokkaaminen tulkkiin lisätään ominaisuuksia yleensä muokkaamalla sitä jollain näistä tavoista: 1 lisätään uusia primitiiviproseduureja 2 lisätään uusia johdettuja lausekkeita (kuten cond tehtiin) 3 lisätään uusia erikoismuotoja (kuten esim. if tehtiin) tässä(kään) ei ole mitään erityisen Scheme-spesistä: samat kolme vaihtoehtoa olisi esim. Python-tulkin muokkaamisessa primitiiviproseduuri riittää, jos uusi lauseke voi evaluoida kaikki argumenttinsa ennen kuin proseduuriin päästään (esim. if ei voi) uusia johdettuja lausekkeita voi tehdä myös tulkkia muuttamatta, jos kieli tukee makroja (lähinnä Lisp-kielet tukevat) ihan kaikkia ominaisuuksia ei voi toteuttaa näin: joskus tulkin rakennetta pitää muuttaa enemmän esim. poikkeusten (try, catch ja throw) toteuttaminen vaatisi enemmän muutoksia, jos alla olevassa kielessä ei olisi vastaavaa ominaisuutta (Schemessä on)

Tulkin muokkaaminen: uusi primitiivi primitiiviproseduureja on helppo lisätä: lisätään vain niiden toteutus primitive-procedures-listaan hankalampaa, jos primiivi ottaa argumentiksi tai palauttaa proseduureja (kuten map) tai uuden primitiivin voisi määritellä tulkin sisällä: esim. kutsua setup-environmentista evalia omalla define-lausekkeella Esimerkki uusien primitiivien lisäämisestä (muutokset punaisella) (define (average x y) (/ (+ x y) 2)) (define primitive-procedures (list... (list 'length length) (list 'average average) (list 'print display)...)) ; Schemen primitiivi ; itse toteutettu primitiivi ; uudelleennimetty Schemen primitiivi

Tulkin muokkaaminen: uusi johdettu lauseke 1 tehdään apuproseduurit, jotka tunnistavat uuden lausekkeen ja jakavat sen osiin 2 tehdään muunnosproseduuri, joka muuntaa uuden lausekkeen koodiksi, jota tulkki ennestään tukee 3 lisätään evaliin haara, joka kutsuu muunnosproseduuria Esimerkki uuden johdetun lausekkeen lisäämisestä ;; käyttöesimerkki: (let ((a 2)) (unless (= a 0) (/ 1 a) 0)) 1/2 (define (unless? exp) (tagged-list? exp 'unless)) (define (unless-predicate exp) (cadr exp)) (define (unless-alternative exp) (caddr exp)) (define (unless-consequent exp) (cadddr exp)) (define (unless->if exp) ; muunnos (unless a b c) (if a c b) (make-if (unless-predicate exp) (unless-consequent exp) (unless-alternative exp))) (define (eval exp env) (cond... ((unless? exp) (eval (unless->if exp) env))... ))

Tulkin muokkaaminen: uusi erikoismuoto 1 tehdään apuproseduurit, jotka tunnistavat uuden lausekkeen ja jakavat sen osiin 2 tehdään proseduuri, joka suorittaa (tulkitsee) uuden lausekkeen 3 lisätään evaliin haara uudelle lausekkeelle Esimerkki uuden erikoismuodon lisäämisestä m-eval-prog.scm ;; (prog n lauseke...): kuten begin, mutta saa valita minkä lausekkeen ;; arvo palautetaan; esim. (let ((x 5)) (prog 1 x (set! x (+ x 1))) 5 (define (prog? exp) (tagged-list? exp 'prog)) (define (prog-n exp) (cadr exp)) (define (prog-exps exp) (cddr exp)) (define (eval-prog n exps env saved) ; ei säilytä häntärekursiota! (if (null? exps) saved (let ((result (eval (car exps) env))) (eval-prog (- n 1) (cdr exps) env (if (= n 1) result saved))))) (define (eval exp env) (cond... ((prog? exp) (eval-prog (prog-n exp) (prog-exps exp) env #f))... ))

Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta: sisäiset määrittelyt (SICP 3.2.4 ja 4.1.6) 3 Makrot 4 Ohjelmia muokkaavat ohjelmat 5 Kierroksen 3 tehtävien ratkaisut

Sisäiset määrittelyt (SICP 3.2.4, 4.1.6) tulkkien ja ympäristöjen yhteydessä ei vielä käsitelty pientä yksityiskohtaa: paikallisten (eli toisen proseduurin sisäisten) proseduurien määrittelyä miten paikallisten proseduurien määrittely definellä vaikuttaa ympäristöihin? define lisää muuttujan sen evaluointiympäristön sisimpään kehykseen (ei siis tee uutta kehystä kuten let) joten sisäiset määrittelyt lisätään proseduuria kutsuttessa luotuun kehykseen (samaan kuin proseduurin argumentit) myös kohdan 4.1 tulkki toimii näin (define-variablessa) entä jos paikalliset proseduurit viittaavat toisiinsa? define lisää ne samaan kehykseen, joten kaikki samalla tasolla määritellyt näkevät toisensa kaikki siis toimii loogisesti, jos defineä käyttää kuten kirjassa on tapana (ensin definet, sitten muu koodi)

Sisäiset määrittelyt: define ja letrec (SICP 3.2.4, 4.1.6) sisäisellä definellä määriteltyjen muuttujien arvoja saa käyttää vasta kun kaikki definet on suoritettu käytännössä proseduurin rungossa on ensin definet paikallisille proseduureille ja sitten vasta muu koodi definellä ei pitäisi määritellä tavallisia muuttujia (vaan letillä) toinen tapa paikallisten proseduurien tekemiseen on letrec kuten let, mutta määritelmien sisällä voi viitata samassa letrecissä määriteltäviin muuttujiin käsitteellisesti siistimpi kuin sisäinen define: paikalliset määrittelyt ja proseduurin varsinainen koodi erottuvat toisistaan define- ja letrec-esimerkki (define (my-odd-1? x) (define (even? n) (if (= n 0) true (odd? (- n 1)))) (define (odd? n) (if (= n 0) false (even? (- n 1)))) (odd? x)) (define (my-odd-2? x) ; sama letrecillä (letrec ((even? (lambda (n) (if (= n 0) true (odd? (- n 1))))) (odd? (lambda (n) (if (= n 0) false (even? (- n 1)))))) (odd? x)))

Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta: sisäiset määrittelyt (SICP 3.2.4 ja 4.1.6) 3 Makrot 4 Ohjelmia muokkaavat ohjelmat 5 Kierroksen 3 tehtävien ratkaisut

Mikä on makro? makroilla ohjelmoija voi määritellä kieleen uutta syntaksia (uusia johdettuja lausekkeita) makron määritelmä kertoo, miten makroja sisältävää ohjelmakoodia muokataan ennen kuin se suoritetaan koodia muutetaan makron käyttöpaikan eli kutsun kohdalta Lispissä makro on käännösaikainen proseduuri, joka saa argumenteiksi lausekkeita ja palauttaa uuden lausekkeen, joka varsinaisesti ajon aikana suoritetaan esim. kirjan tulkin cond->if-muunnosproseduuri toimisi sellaisenaan makrona: cond->if saa argumentiksi listan lausekkeita (condin ehdot ja niiden koodin) ja tuottaa lausekkeen, jossa on sisäkkäisiä iffejä lauseke muunnetaan eli makro lavennetaan joko käännösaikana tai (tulkissa) juuri ennen koodin suoritusta makroja käytetään varsinkin sellaisten abstraktioiden tekemiseen, joihin proseduurit eivät riitä

Makrot Lisp-kielissä: define-macro useimmille Lisp-kielille yhteinen tapa määritellä makroja on define-macro (joissain kielissä nimenä on defmacro) (define-macro ( makron nimi argumentit... ) koodi ) määrittelee uuden makron koodi saa argumenteikseen makron kutsussa olevat lausekkeet, ja sen pitäisi palauttaa lauseke, johon makro lavenee koodi on siis tavallista kielen koodia, joka käsittelee kielen lausekkeita (ja joka voidaan suorittaa käännösaikana) Scheme-esimerkkejä (define-macro (unless predicate alternative consequent) (list 'if predicate consequent alternative)) (unless (< 1 2) 3 4) 4 (define-macro (ind n. exprs) (if (null? exprs) 0 (list 'if (car exprs) n (cons 'ind (cons (+ n 1) (cdr exprs)))))) (ind 1 (< 2 1) (< 3 4)) (if (< 2 1) 1 (if (< 3 4) 2 0)) 2

quasiquote quasiquote eli ` on apusyntaksi sellaisten listojen rakentamiseen, joista osa pysyy vakiona ja osa lasketaan ajon aikana quasiquotea voi käyttää ilmankin makroja sen voisi aina kirjoittaa auki mm. quoten ja consin avulla quasiquote toimii kuten quote, paitsi: jos listassa on, eli unquote, sen jälkeen oleva lauseke evaluoidaan normaalisti ja sen paluuarvo tulee listan alkioksi,@ eli unquote-splicing on kuten,, mutta evaluoitavan lausekkeen palauttaman listan alkiot liitetään listan tähän kohtaan sisäkkäiset quasiquotet ovat vähän monimutkaisempia Esimerkkejä quasiquotesta `(a (+ 5 2) c) (a (+ 5 2) c) `(a,(+ 5 2) c) (a 7 c) `(a,(filter odd '(1 2 3)) c) (a (1 3) c) `(a,@(filter odd '(1 2 3)) c) (a 1 3 c) (define-macro (ind n. exprs) (if (null? exprs) 0 `(if,(car exprs),n (ind,(+ n 1),@(cdr exprs)))))

Tapa tehdä uusia muuttujannimiä: gensym Viallinen esimerkki or-makrosta or-macro.scm (define-macro (my-or. exprs) (cond ((null? exprs) #f) ((null? (cdr exprs)) (car exprs)) (else `(let ((x,(car exprs))) (if x x (my-or,@(cdr exprs))))))) (my-or (= 1 2) (= 3 4)) #f ; tämä vielä toimii (let ((x 2)) (my-or (= x 3) (= x 2))) ; tästä tulee virheilmoitus! vika tulee siitä, että makro määrittelee muuttujan x, jota käytetään sen argumentissa eri merkityksessä korjaus: primitiivi (gensym) tuottaa nimeksi uuden symbolin Korjattu esimerkki or-macro.scm (define-macro (my-or. exprs) (cond ((null? exprs) #f) ((null? (cdr exprs)) (car exprs)) (else (let ((name (gensym))) `(let ((,name,(car exprs))) (if,name,name (my-or,@(cdr exprs))))))))

R 5 RS-Schemen hygieeniset makrot standardi-schemessä ei ole define-macroa vaan oma makrojärjestelmä Schemen makrot ovat hygieenisiä eli nimeävät makron paikalliset muuttujat uudelleen automaattisesti koodi ei ole Scheme-koodia, vaan vain lauseke, johon makro laventuu (kylläkin rekursiivisesti) R 6 RS-Schemessä monipuolisempi makrokielessä on hahmonsovitus, jota ei kuitenkaan voi käyttää muussa Schemessä käytännössä Scheme-makroja käytetään melko vähän Scheme-makroesimerkkejä ;; or Scheme-standardista ;;... on makrokielen koodia! (define-syntax or (syntax-rules () ((or) #f) ((or test) test) ((or test1 test2...) (let ((x test1)) (if x x (or test2...)))))) ;; let Scheme-standardista ;; (osa: nimetty let puuttuu) (define-syntax let (syntax-rules () ((let ((name val)...) body1 body2...) ((lambda (name...) body1 body2...) val...))))

Entä C:n makrot? C- ja C++-kielissä makrot muokkaavat merkkijonoja eivätkä varsinaisia kielen lausekkeita merkkijonosta tehdään lauseke (eli se jäsennetään, parse) vasta makrolavennuksen jälkeen poikkeuksena makroja määrittelevä yms. koodi (# rivin alussa) C:n makroilla voi tehdä lähinnä vakiolavennuksia ja yksinkertaisia ehtolauseita (#if), Lispin/Schemen makroissa voi käyttää koko ohjelmointikieltä joten Lisp-kääntäjän pitää itse suorittaa kääntämäänsä kieltä yleensä Lispeissä on erillinen tapa suorittaa koodia käännösaikana muutenkin kuin makron sisällä näistä syistä Lispin makroilla tehdään monimutkaisempia asioita kuin esim. C:n makroilla mm. Common Lispin oliojärjestelmän voi toteuttaa makroilla

Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta: sisäiset määrittelyt (SICP 3.2.4 ja 4.1.6) 3 Makrot 4 Ohjelmia muokkaavat ohjelmat 5 Kierroksen 3 tehtävien ratkaisut

Ohjelmia muokkaavat ohjelmat kirjan tulkit suorittavat annettua Scheme- tms. ohjelmaa samaan tapaan voi myös muuttaa annettua ohjelmaa muunnosproseduuri ottaa ohjelman eli lausekkeen argumentiksi ja palauttaa muokatun version siitä jaetaan käsittely osiin lauseketyypin (set!, if jne.) mukaan samoin kuin tulkissa mutta lausekkeen suorittamisen sijaan palautetaan lauseke joko sellaisenaan tai muunnettuna alilausekkeet muunnetaan rekursiivisesti tai tutkia sitä: esimerkiksi tarkistaa osia koodin toiminnasta staattisesti eli suorittamatta sitä esim. Scheme-koodista voi tarkistaa, käytetäänkö siinä muuttujia, joita ei ole määritelty tai koodista voisi optimointeja varten etsiä muuttujia, joiden arvoja ei koskaan muuteta, tai sivuvaikutuksettomia proseduureja tai kääntää sen toiselle (yleensä matalamman tason) kielelle

Ohjelman muokkauksen käyttötarkoituksia mitä ohjelmaa muokkaamalla sitten voi tehdä? kääntää koodia yksinkertaisempaan muotoon, esim. poistaa cond-lausekkeet erilaisia optimointeja lisätä koodiin dynaamisia eli ajonaikaisia tarkistuksia muuttaa koodin toimintaa (esim. tehdä kaikkien tulostuskomentojen tilalla jotain muuta) lisätä toiminnallisuutta, esim. kerätä tietoja siitä, mitä koodia suoritetaan eniten kääntäjät tekevät usein tällaisia muunnoksia ennen varsinaista kääntämistä, joskus sen jälkeenkin

Scheme-ohjelman muokkausproseduurin (eräs) runko Kopioi lausekkeen ja cond->if-muuntaa sen syntax-process.scm ;; käyttää kirjan tulkin apuproseduureja tagged-list? ja cond->if (define (process exp) (cond ((tagged-list? exp 'quote) exp) ((tagged-list? exp 'set!) (list 'set! (cadr exp) (process (caddr exp)))) ((tagged-list? exp 'define) (cons 'define (cons (cadr exp) (map process (cddr exp))))) ((tagged-list? exp 'if) (cons 'if (map process (cdr exp)))) ((tagged-list? exp 'lambda) (cons 'lambda (cons (cadr exp) (map process (cddr exp))))) ((tagged-list? exp 'begin) (cons 'begin (map process (cdr exp)))) ((tagged-list? exp 'cond) (process (cond->if exp))) ((tagged-list? exp 'let) (cons 'let (cons (map (lambda (clause) (list (car clause) (process (cadr clause)))) (cadr exp)) (map process (cddr exp))))) ((pair? exp) (map process exp)) (else exp)))

Muunnosesimerkki: tilastoja if:ien ehdoista Muutokset edelliseen punaisella syntax-process.scm (define predicate-id 0) (define (process exp) (cond... ((tagged-list? exp 'if) (set! predicate-id (+ predicate-id 1)) (cons 'if (cons (list 'predicate-stat predicate-id (process (cadr exp))) (map process (cddr exp)))))...)) lisää jokaisen if-lausekkeen ehtoon tilastointiproseduurin kutsun esim. (if (< x 0) a b) (if (predicate-stat 1 (< x 0)) a b) muunnettua koodia ajettaessa predicate-stat voisi esim. kerätä tilastoja siitä, kuinka usein mitäkin haaraa suoritetaan (define (predicate-stat n branch) (add-to-statistics! n branch) branch)

Sisältö 1 Kirjan tulkin muokkaaminen 2 Yksityiskohta: sisäiset määrittelyt (SICP 3.2.4 ja 4.1.6) 3 Makrot 4 Ohjelmia muokkaavat ohjelmat 5 Kierroksen 3 tehtävien ratkaisut