Ohjelmoinnin peruskurssien laaja oppimäärä

Samankaltaiset tiedostot
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 5

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 4

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 1

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 7 Vastaukset

Ohjelmoinnin peruskurssien laaja oppimäärä

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 2

Ohjelmoinnin peruskurssien laaja oppimäärä

815338A Ohjelmointikielten periaatteet

Scheme-kesäkurssi luento 6

Hohde Consulting 2004

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmointiharjoituksia Arduino-ympäristössä

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

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

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Geneeriset tyypit. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos

Makrojen mystinen maailma lyhyt oppimäärä

ICS-C2000 Tietojenkäsittelyteoria Kevät 2016

Luku 3. Listankäsittelyä. 3.1 Listat

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin perusteet Y Python

Luento 4 Aliohjelmien toteutus

TIEP114 Tietokoneen rakenne ja arkkitehtuuri, 3 op. Assembly ja konekieli

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

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin peruskurssien laaja oppimäärä

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Jakso 4 Aliohjelmien toteutus

Funktionaalinen ohjelmointi

Taas laskin. TIES341 Funktio ohjelmointi 2 Kevät 2006

Bootstrap / HTDP2 / Realm of Racket. Vertailu

11/20: Konepelti auki

811120P Diskreetit rakenteet

Ohjelmoinnin perusteet Y Python

TIEP114 Tietokoneen rakenne ja arkkitehtuuri, 3 op. Assembly ja konekieli

A TIETORAKENTEET JA ALGORITMIT

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

Luento 5. Timo Savola. 28. huhtikuuta 2006

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

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

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

ITKP102 Ohjelmointi 1 (6 op)

Tietorakenteet ja algoritmit - syksy

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Luento 4 Aliohjelmien toteutus

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

Ohjelmoinnin peruskurssien laaja oppimäärä

Ruby. Tampere University of Technology Department of Pervasive Computing TIE Principles of Programming Languages

7/20: Paketti kasassa ensimmäistä kertaa

Funktionaalinen ohjelmointi

C++11 lambdat: [](){} Matti Rintala

5.5 Jäsenninkombinaattoreista

Matlab- ja Maple- ohjelmointi

Haskell ohjelmointikielen tyyppijärjestelmä

Tutoriaaliläsnäoloista

1.3Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

Funktionaalisten kielten ydinpiirteiden toteutus oliokielten virtuaalikonealustoilla

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 19. tammikuuta 2012

Jakso 4 Aliohjelmien toteutus

Mathematica Sekalaista asiaa

Javascript 2: Ohjelmointikielen ominaisuudet. Jaana Holvikivi Metropolia

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

2. Seuraavassa kuvassa on verkon solmujen topologinen järjestys: x t v q z u s y w r. Kuva 1: Tehtävän 2 solmut järjestettynä topologisesti.

Ohjelmien analysointi. ER-kaaviot

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Lisää pysähtymisaiheisia ongelmia

Transkriptio:

Ohjelmoinnin peruskurssien laaja oppimäärä Luento 8: Pienen ohjelmointikielen tulkki (ohjelmoitava laskin) (mm. SICP 4-4.1.5 osin) Riku Saikkonen 15. 11. 2012

Sisältö 1 Nelilaskintulkki, globaalit muuttujat 2 Proseduurit ja if 3 Abstrakti syntaksi ja env-argumentti

Miksi tulkkeja? tulkki näkemällä on helppo ymmärtää ohjelmointikielten toimintaa tulkki kertoo kielen semantiikasta eli merkityksestä konkreettisesti (vaikka oikea kielen toteutus tekeekin enemmän optimointeja) melkein minkä tahansa kielen tulkki on hyvin samankaltainen kuin SICP-kirjan Scheme-tulkki eri kielten erikoisuuksia ja rajoituksia on helpompi ymmärtää, kun tietää, miten niitä toteutetaan tulkilla voi kokeilla ohjelmointikielen muokkaamista ja oman kielen suunnittelua tulkki on vain yksi ohjelma muiden joukossa kannattaa ajatella itseään myös ohjelmointikielten suunnittelijana eikä vain olemassaolevien kielten käyttäjänä (samoin kirjastojen) usein isoihin kirjastoihin ja apuohjelmiin suunnitellaan omia kieliä (proseduureilla, makroilla, tulkilla tai kääntäjällä) tulkin rakennetta voi käyttää pohjana muuhun kielen käsittelyyn, esim. asetustiedoston lukemiseen

Yksinkertainen nelilaskintulkki aloitetaan rakentamalla tulkki, joka osaa laskea matemaattisia lausekkeita, joissa on operaattoreita +, -, * ja / lisätään tähän sen jälkeen vähitellen ominaisuuksia Perustulkin koko koodi nelilaskin1.scm (define (eval exp) (cond ((number? exp) exp) ((pair? exp) (cond ((eq? (car exp) '+) (+ (eval (cadr exp)) (eval (caddr exp)))) ((eq? (car exp) '-) (- (eval (cadr exp)) (eval (caddr exp)))) ((eq? (car exp) '*) (* (eval (cadr exp)) (eval (caddr exp)))) ((eq? (car exp) '/) (/ (eval (cadr exp)) (eval (caddr exp)))) (else (error "Invalid operation -- EVAL" exp)))) (else (error "Invalid expression -- EVAL" exp)))) Testiajo: (eval '(+ (* 2 4) (- 6 5))) 9

Abstraktio: lista primitiiveistä Tulkin koko koodi (muutokset punaisella) nelilaskin2.scm (define primitive-names (list '+ '- '* '/)) (define primitive-values (list + - * /)) (define (find-primitive name) (define (scan names values) (cond ((null? names) (error "Invalid calculation -- FIND-PRIMITIVE" name)) ((eq? (car names) name) (car values)) (else (scan (cdr names) (cdr values))))) (scan primitive-names primitive-values)) (define (eval exp) (cond ((number? exp) exp) ((pair? exp) (let ((proc (find-primitive (car exp)))) (proc (eval (cadr exp)) (eval (caddr exp))))) (else (error "Invalid expression -- EVAL" exp))))

Lisätään itse määritellyt muuttujat 1/2 Lisäyksiä edelliseen koodiin 1/2 nelilaskin3.scm (define variable-names nil) (define variable-values nil) (define (lookup-variable-value var) (define (scan names values) (cond ((null? names) (error "Variable not found -- LOOKUP-VARIABLE-VALUE" var)) ((eq? (car names) var) (car values)) (else (scan (cdr names) (cdr values))))) (scan variable-names variable-values)) (define (set-variable-value! var val) (define (scan names values) (cond ((null? names) (error "Variable not found -- SET-VARIABLE-VALUE!" var)) ((eq? (car names) var) (set-car! values val)) (else (scan (cdr names) (cdr values))))) (scan variable-names variable-values))

Lisätään itse määritellyt muuttujat 2/2 Lisäyksiä edelliseen koodiin 2/2 nelilaskin3.scm (define (define-variable! var val) (set! variable-names (cons var variable-names)) (set! variable-values (cons val variable-values))) (define (eval exp) (cond... ((symbol? exp) (lookup-variable-value exp)) ((and (pair? exp) (eq? (car exp) 'set!)) (set-variable-value! (cadr exp) (eval (caddr exp)))) ((and (pair? exp) (eq? (car exp) 'define)) (define-variable! (cadr exp) (eval (caddr exp))))... )) Testiajoja (yllä keksittiin myös syntaksit muuttujien käsittelyyn): (eval '(define x 4)) (eval '(set! x (+ x 1))) (eval '(+ (- x 1) (- 2 (* x x)))) -19

Koodin siistimistä: primitiivit voivat olla muuttujia find-primitive ja lookup-variable-value tekevät melkein saman, mutta lukevat eri listoja yhtä hyvin primitiivit voisivat olla vain muuttujia joilla on hassu arvo (Schemessä näin on, mutta kaikissa kielissä ei) lisäksi voisi haluta estää niiden uudelleenmäärittelyn (ei tässä) Muutokset edelliseen koodiin nelilaskin4.scm ;; ota primitive-names, primitive-values ja find-primitive kokonaan pois ;; ja muokkaa: (define variable-names (list '+ '- '* '/)) (define variable-values (list + - * /)) (define (eval exp) (cond... ((pair? exp) ;; seuraavassa on vain muutettu find-primitive eval ;; se voisi olla myös lookup-variable-value (ei Schemessä, miksi?) (let ((proc (eval (car exp)))) (proc (eval (cadr exp)) (eval (caddr exp)))))... ))

Sisältö 1 Nelilaskintulkki, globaalit muuttujat 2 Proseduurit ja if 3 Abstrakti syntaksi ja env-argumentti

Lisätään if (helppoa!) (SICP 4.1.1) Muutokset edelliseen koodiin nelilaskin5.scm (define variable-names (list '+ '- '* '/ '< '= '> 'true 'false)) (define variable-values (list + - * / < = > true false)) ; tai #t #f ;; otetaan kantaa siihen, mikä tulkattavassa kielessä on ''totta'' (define (true? x) (not (eq? x false))) (define (eval exp) (cond... ((and (pair? exp) (eq? (car exp) 'if)) (if (true? (eval (cadr exp))) (eval (caddr exp)) (eval (cadddr exp))))... )) Testiajoja: (eval '(define x 5)) (eval '(if (> x 3) 1 2)) 1 (eval '(define y 0)) (eval '(set! x (if (> y 0) (/ 1 y) 0))) ; x on nyt 0

Lisätään argumentittomat proseduurit ja begin 1/2 lisätään tulkkiimme proseduurit (taas Schemen näköisellä syntaksilla), mutta ei mietitä paikallisia muuttujia vielä tällä tulkilla voi teoriassa jo laskea mitä tahansa (sillä tulkattava kieli on Turing-täydellinen, jos numeroarvoilla ei ole ylärajaa), mutta pelkillä globaaleilla muuttujilla koodi on BASIC-mäistä... Testiajoja (koodi seuraavalla kalvolla) nelilaskin6.scm (eval '(define fact-iter ; vrt. factorial SICP 3.1.3:n lopussa (lambda () ; argumenttilistaa ei vielä käytetä mihinkään (if (> c n) p (begin (set! p (* c p)) (set! c (+ c 1)) (fact-iter)))))) (eval '(define p 1)) (eval '(define c 1)) (eval '(define n 5)) (eval '(fact-iter)) 120

Lisätään argumentittomat proseduurit ja begin 2/2 Muutokset edelliseen koodiin (tämä riittää!) nelilaskin6.scm (define (eval-sequence exps) ; suorittaa exps-lausekkeet järjestyksessä (cond ((null? (cdr exps)) ; onko viimeinen lauseke? (eval (car exps))) (else (eval (car exps)) (eval-sequence (cdr exps))))) (define (eval exp) (cond... ((and (pair? exp) (eq? (car exp) 'lambda)) (list 'procedure (cddr exp))) ; proseduurin ''arvo'' ((and (pair? exp) (eq? (car exp) 'begin)) (eval-sequence (cdr exp))) ((pair? exp) (let ((proc (eval (car exp)))) (if (and (pair? proc) (eq? (car proc) 'procedure)) (eval-sequence (cadr proc)) (proc (eval (cadr exp)) (eval (caddr exp))))))... ))

Lisätään vielä käyttöliittymä (SICP 4.1.4) jotta tulkkimme näyttäisi tulkilta, lisätään siihen vielä käyttöliittymä eli silmukka, joka lukee lausekkeen, suorittaa sen, ja kertoo laskennan tuloksen eli paluuarvon Schemessä (Lispissä) tällainen on nimeltään readevalprint loop tämä on helppoa, koska Schemessä on read-primitiivi Lisäyksiä edelliseen koodiin nelilaskin7.scm (define input-prompt ";;; Eval input:") (define output-prompt ";;; Eval value:") (define (driver-loop) (prompt-for-input input-prompt) (let ((input (read))) (let ((output (eval input))) (announce-output output-prompt) (display output))) (driver-loop)) (define (prompt-for-input string) (newline) (newline) (display string) (newline)) (define (announce-output string) (newline) (display string) (newline))

Mihin pääsimme? tulkissamme on yhteensä 64 riviä Schemeä, ja se tulkitsee jo periaatteessa täydellistä ohjelmointikieltä oikeastaan se kaipaisi vielä tietorakenteita, mutta ne saa helposti lisäämällä primitiivilistaan esim. cons, car, cdr ja null? tulkki tukee toistaiseksi vain globaaleja muuttujia: lisäämme paikalliset muuttujat myöhemmin... tulkattavan kielemme syntaksi muistuttaa Schemeä, mutta olisimme yhtä hyvin voineet tehdä siitä erilaisen kunhan syntaksi on sellainen että read tuottaa siitä listarakenteen esim. (if (< x 4) then (x = (+ x 1)) else (x = 9)) olisi melko helppo toteuttaa

Onko Scheme-tulkki aina näin yksinkertainen? tulkkimme lainaa alla olevalta Scheme-toteutukselta mm: muistinhallinnan (lausekkeen tallennus, uuden muuttujan ja arvon tallennus, käyttämättömän muistin vapautus,... ) numeroiden tallennuksen ja primitiivioperaatiot tuen proseduurikutsuille ja rekursiolle (tulkki kutsuu itseään rekursiivisesti) häntärekursio-optimoinnin (tulkki toimii häntärekursiivisesti, jos alla oleva Scheme toimii niin) lausekkeiden esitysmuodon listarakenteena ja readin jos alla oleva kieli ei tukisi näitä, niitä pitäisi toteuttaa ja tulkin koodi monimutkaistuisi tai tulkattavaan kieleen tulisi rajoituksia, esim: ei häntärekursio-optimointia (joten kieleen pitäisi tehdä silmukkarakenteita tai goto) luvuille rajattu lukualue (esim. [ 2 31 1.. 2 31 ]) muistia pitäisi erikseen vapauttaa

Sisältö 1 Nelilaskintulkki, globaalit muuttujat 2 Proseduurit ja if 3 Abstrakti syntaksi ja env-argumentti

Mitä seuraavaksi (seuraavalla luennolla)? tehdään seuraavaksi abstraktiot tulkattavan kielen syntaksin käsittelylle, jotta syntaksia olisi periaatteessa helppo muuttaa samalla koodi muuttuu lähemmäs SICP-kirjan tulkin koodia... pidetään tulkin toiminta muuten täsmälleen samana, mutta lisätään tässä samalla tuki: else-haarattomalle if:lle (palauttaa false) primitiiveille, joilla on enemmän tai vähemmän kuin kaksi argumenttia (define (f)...)-syntaksille: kuten Schemessä, se on sama kuin (define f (lambda ()...)) luovutaan myös globaaleista muuttujista variable-names ja variable-values ja kuljetetaan niitä tulkkiproseduurien uudessa argumentissa env toistaiseksi env osoittaa koko ajan samaan tietorakenteeseen (tätä muokataan myöhemmin paikallisten muuttujien tukemiseksi)

Entinen eval Koko eval ennen tätä muutosta nelilaskin7.scm (define (eval exp) (cond ((number? exp) exp) ((symbol? exp) (lookup-variable-value exp)) ((and (pair? exp) (eq? (car exp) 'set!)) (set-variable-value! (cadr exp) (eval (caddr exp)))) ((and (pair? exp) (eq? (car exp) 'define)) (define-variable! (cadr exp) (eval (caddr exp)))) ((and (pair? exp) (eq? (car exp) 'if)) (if (true? (eval (cadr exp))) (eval (caddr exp)) (eval (cadddr exp)))) ((and (pair? exp) (eq? (car exp) 'lambda)) (list 'procedure (cddr exp))) ((and (pair? exp) (eq? (car exp) 'begin)) (eval-sequence (cdr exp))) ((pair? exp) (let ((proc (eval (car exp)))) (if (and (pair? proc) (eq? (car proc) 'procedure)) (eval-sequence (cadr proc)) (proc (eval (cadr exp)) (eval (caddr exp)))))) (else (error "Invalid expression -- EVAL" exp))))

Uusi eval (tarkemmin seuraavalla luennolla) Koko eval tämän muutoksen jälkeen nelilaskin8.scm (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((assignment? exp) (set-variable-value! (assignment-variable exp) (eval (assignment-value exp) env) env)) ((definition? exp) (define-variable! (definition-variable exp) (eval (definition-value exp) env) env)) ((if? exp) (if (true? (eval (if-predicate exp) env)) (eval (if-consequent exp) env) (eval (if-alternative exp) env))) ((lambda? exp) (make-procedure (lambda-body exp))) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((application? exp) (let ((proc (eval (operator exp) env))) (if (compound-procedure? proc) (eval-sequence (procedure-body proc) env) (apply proc (map (lambda (exp) (eval exp env)) (operands exp)))))) (else (error "Unknown expression type -- EVAL" exp))))