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ä

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ä

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 2

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Scheme-kesäkurssi luento 3

815338A Ohjelmointikielten periaatteet

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 5

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin peruskurssien laaja oppimäärä

Luku 3. Listankäsittelyä. 3.1 Listat

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Laiska laskenta, korekursio ja äärettömyys. TIEA341 Funktio ohjelmointi Syksy 2005

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Tyyppejä ja vähän muutakin. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Scheme-kesäkurssi luento 1

Olio-ohjelmointi Syntaksikokoelma

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin peruskurssien laaja oppimäärä

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

Funktionaalinen ohjelmointi

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

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Makrojen mystinen maailma lyhyt oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Tämän vuoksi kannattaa ottaa käytännöksi aina kirjoittaa uuden funktion tyyppi näkyviin, ennen kuin alkaa sen määritemää kirjoittamaan.

Ohjelmoinnin peruskurssien laaja oppimäärä

Luento 5. Timo Savola. 28. huhtikuuta 2006

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

Tutoriaaliläsnäoloista

Haskell ohjelmointikielen tyyppijärjestelmä

Funktionaalinen ohjelmointi

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Bootstrap / HTDP2 / Realm of Racket. Vertailu

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

Funktionaalisten kielten oppimisesta ja valinnasta

TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia

Laajennetaan vielä Ydin-Haskellia ymmärtämään vakiomäärittelyt. Määrittely on muotoa

Tietorakenteet ja algoritmit - syksy

Hohde Consulting 2004

Monadeja siellä, monadeja täällä... monadeja kaikkialla? TIES341 Funktio ohjelmointi 2 Kevät 2006

Ohjelmoinnin perusteet Y Python

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

815338A Ohjelmointikielten periaatteet

Ohjelmoinnin perusteet Y Python

Dynaamista ja joustavaa ohjelmointia - maukasta makrokielellä

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

5.5 Jäsenninkombinaattoreista

Ohjelmoinnin peruskurssi Y1

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

Koka. Ryhmä 11. Juuso Tapaninen, Akseli Karvinen. 1. Taustoja 2. Kielen filosofia ja paradigmat 3. Kielen syntaksia ja vertailua JavaScriptiin Lähteet

Ohjelmoinnin perusteet Y Python

13. Loogiset operaatiot 13.1

Ohjelmointiharjoituksia Arduino-ympäristössä

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

TAMPEREEN TEKNILLINEN YLIOPISTO

Ohjelmoinnin perusteet Y Python

Java-kielen perusteet

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

Ohjelmoinnin peruskurssi Y1

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

Mitä funktionaalinen ohjelmointi on

Ohjelmoinnin peruskurssi Y1

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Jakso 4 Aliohjelmien toteutus

Tietorakenteet ja algoritmit

Ohjelmoinnin peruskurssien laaja oppimäärä

Transkriptio:

Ohjelmoinnin peruskurssien laaja oppimäärä Luento 7: Funktionaalista ohjelmointia (mm. SICP 3.5) Riku Saikkonen 13. 11. 2012

Sisältö 1 Laiskaa laskentaa: delay ja force 2 Funktionaalinen I/O 3 Funktionaalista ohjelmointia: hahmonsovitussyntaksi 4 Funktionaalista ohjelmointia: list comprehension -rakenne

Schemen delay ja force laiska laskenta (lazy evaluation tai non-strict): lauseke lasketaan vasta sitten kun (ja jos) sen arvoa tarvitaan tavallisessa Schemessä laiskaa laskentaa voi tehdä delay- ja force-primitiiveillä (delay lauseke ) palauttaa erikoisarvon, joka kuvaa vielä laskematta olevaa lausekketta (force jonkin delayn paluuarvo ) pakottaa lausekkeen laskemisen ja palauttaa sen arvon lauseke evaluoidaan vain kerran: jos forcelle antaa jo kerran lasketun lausekkeen, se palauttaa saman arvon uudelleen virrat on toteutettu delayn ja forcen avulla: (cons-stream a b ) (cons a (delay b )) samantapainen rakenne on muutamissa muissakin kielissä (mm. Scala ja jotkut ML-toteutukset), ja moniin se on helppo toteuttaa itse (esim. Ruby ja Common Lisp) mm. Haskell-kielessä lausekkeet lasketaan aina laiskasti

delayn ja forcen toteuttaminen Schemessä (SICP 3.5.1) jos Schemessä ei olisi delaytä ja forcea, ne voisi toteuttaa muun Schemen avulla: (delay a ):n sijaan sanotaan (memo-proc (lambda () a )) tällöin forcen voi toteuttaa (define (force a) (a)) delaytä ei voi tehdä tähän mennessä opitulla Schemellä, mutta sen voisi tehdä makrona alla olevalla koodilla delayn ja forcen toteuttaminen (define (memo-proc proc) (let ((already-run? false) (result false)) (lambda () (if (not already-run?) (begin (set! result (proc)) (set! already-run? true) result) result)))) (define-macro (delay a) `(memo-proc (lambda (),a))) ; Lisp-makro (define (force a) (a))

Sisältö 1 Laiskaa laskentaa: delay ja force 2 Funktionaalinen I/O 3 Funktionaalista ohjelmointia: hahmonsovitussyntaksi 4 Funktionaalista ohjelmointia: list comprehension -rakenne

I/O:n ongelma (osin SICP 3.5.5) puhtaasti funktionaalisessa ohjelmoinnissa millään operaatiolla ei saisi olla sivuvaikutuksia laskennan tilan ylläpitoa voi tehdä mm. rekursiolla ja virroilla entä interaktio käyttäjän (tai esim. verkon) kanssa? toinen vastaava ongelma on tilallinen funktio, jonka sisäistä tilaa ei haluaisi käsitellä koko ajan: esim. satunnaislukugeneraattori funktionaaliseen I/O:hon ei ole täydellistä ratkaisua (vielä?) perusratkaisu on, että pääohjelma on funktio syötevirralta tulosvirralle (esimerkki kohta) Haskell-kielessä tämän idean päälle on rakennettu koodia siistivä mutta käsitteellisesti monimutkainen abstraktio nimeltä monadi toinen ratkaisu (esim. Clean-kielessä) on tyyppijärjestelmän laajennus niin, että tiettyjä arvoja voi käyttää vain kerran (uniqueness type) tätä ongelmaa tutkitaan vielä... käytännön ratkaisu on usein eristää I/O:ta tekevä koodi muusta enemmän funktionaalisesta koodista

I/O virroilla: toteutus Virta-I/O:n toteuttaminen Schemessä (define (run-io io-proc) (define (get-input-stream) (cons-stream (delay (read)) (get-input-stream))) (define (display-output-stream stream) (if (stream-null? stream) 'end (begin (display (stream-car stream)) (newline) (display-output-stream (stream-cdr stream))))) (display-output-stream (io-proc (get-input-stream)))) interaktiivinen pääohjelma on (run-io io-proc), jossa io-proc on oma puhtaasti funktionaalinen proseduuri, joka ottaa argumentiksi virran käyttäjältä tulevia syötteitä (read:lla luettuja Scheme-arvoja) palauttaa virran tulosteita (tulostettavaksi display:llä) run-io tulostaa tulosvirtaa aina kun sitä on saatavilla, ja tuottaa lisää syötevirtaa io-proc:n sitä pyytäessä

I/O virroilla: käyttöesimerkki Edellisen toteutuksen käyttäminen (define (io-sqrt input-stream) ; palauttaa tulostevirran (cons-stream "Enter a number or q to quit:" (let ((input (force (stream-car input-stream)))) (if (eq? input 'q) the-empty-stream (cons-stream "The square root is:" (cons-stream (sqrt input) (io-sqrt (stream-cdr input-stream)))))))) iosqrt.scm (run-io io-sqrt) ; ''pääohjelman'' käynnistys tulostaa siis tulosvirtaan sekä kehotteita että laskemisen tuloksia tässä esimerkissä virta, jonka car olisi myös viivästetty, toimisi paremmin kuin SICP-kirjan virta: siksi force ja edellä delay

I/O virroilla: tilallinen käyttöesimerkki (osin SICP 3.3.5) Pankkitili, jolta voi nostaa ja tallettaa rahaa iobank.scm (define (io-bank balance input-stream) ; tila on balance:ssa (cons-stream (string-append "Current balance: " (number->string balance) " euros") (let ((input (force (stream-car input-stream)))) (if (eq? input 'q) the-empty-stream (let ((new-balance (+ balance input))) (if (< new-balance 0) (cons-stream "Error: not enough money!" (io-bank balance (stream-cdr input-stream))) (cons-stream (string-append (if (< input 0) "Ok, withdrawed " "Ok, deposited ") (number->string input) " euros.") (io-bank new-balance (stream-cdr input-stream))))))))) (define (io-bank-run input-stream) (io-bank 0 input-stream)) (run-io io-bank-run) ; apuproseduuri käynnistykseen

Sisältö 1 Laiskaa laskentaa: delay ja force 2 Funktionaalinen I/O 3 Funktionaalista ohjelmointia: hahmonsovitussyntaksi 4 Funktionaalista ohjelmointia: list comprehension -rakenne

Hahmonsovitus (pattern matching) funktionaalisissa kielissä suosittu erikoissyntaksi (ei Schemessä) funktion määrittelyssä voi olla argumentin sijasta hahmo, johon argumentin tulee sopia ja joka voi asettaa muuttujille arvoja samalle funktiolle voi olla useampia hahmoja, jotka käydään läpi järjestyksessä esimerkiksi (nämä esimerkit ovat Haskell-kieltä): fact 0 = 1 fact n = n * fact (n-1) hahmonsovitus muuttuu periaatteessa if-lauseiksi, jotka kokeilevat hahmoja (argumentit vasemmalta oikealle, hahmot ylhäältä alas): fact n = if n == 0 then 1 else n * fact (n-1) hahmonsovitus on yleensä mahdollista myös funktion sisällä (esim. match) sekä let- ja lambda-lauseissa vastaava idea säännöllisille lausekkeille löytyy esim. Perl-kielestä tässä hahmoon sovitetaan merkkijonoja eikä tietotyyppejä Perl-esimerkki: ($hour, $min, $sec) = ($time = /([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/);

Hahmonsovitus: rakenteet hahmossa voi olla myös listarakenne: sumlist [] = 0 sumlist (x:xs) = x + sumlist xs tässä [] = tyhjä lista ja (x:xs) = pari, jonka car ja cdr talletetaan muuttujiin x ja xs tai itsemääritellyistä tyypeistä koostuva rakenne: data Tree a = Leaf a Branch (Tree a) (Tree a) fringe (Leaf x) = [x] fringe (Branch left right) = fringe left ++ fringe right erikoisuuksia: _ sopii mihin tahansa arvoon, ei tee muuttujaa; syntaksissa xl@(x:xs) koko lista on xl car (x:_) = x cdr (_:x) = x f xl@(x:xs) = xl ++ xs ++ [x] Testiajo: f [1,2,3] [1,2,3,2,3,1]

Vahdit (guard) hahmon lisäksi sovitukseen voi määritellä ehtoja, joiden on oltava tosia, jotta arvo sopisi hahmoon: sign x x > 0 = 1 x == 0 = 0 x < 0 = -1 viimeinen haara voisi olla myös otherwise = -1 vahdit tarkistetaan varsinaisen sovituksen jälkeen, joten sovituksen määrittelemiä muuttujia voi käyttää (kuten x:ää yllä) Monimutkaisempi Haskell-esimerkki mergenodups [] ys = ys mergenodups xs [] = xs mergenodups xl@(x:xs) yl@(y:ys) x < y = x : mergenodups xs yl x == y = mergenodups xs yl otherwise = y : mergenodups xl ys

Hahmonsovitus käytännössä hahmonsovitus on Haskellin lisäksi käytössä esim. ML:ssä, Ocamlissa, Erlangissa, Prologissa ja (osin) Scalassa säännöllisiin lausekkeisiin perustuva hahmonsovitus monessa muussakin kielessä ehkä hahmonsovitus leviää vähitellen uusiin kieliin? hahmonsovituksen etu: koodi on lyhyempää ja lähempänä matemaattista määritelmää (siis helppolukuisempaa, ainakin matemaatikolle... ) hahmonsovituksen ongelmia: ei toimi hyvin abstraktien tietotyyppien kanssa: hahmoa kirjoittaessa pitää tietää mitä kaikkia osia tietotyypissä on ja missä järjestyksessä syntaksi toimii siististi vain lyhyillä hahmoilla ja vahdeilla (hahmonsovitus on uusi erikseen opeteltava ominaisuus)

Sisältö 1 Laiskaa laskentaa: delay ja force 2 Funktionaalinen I/O 3 Funktionaalista ohjelmointia: hahmonsovitussyntaksi 4 Funktionaalista ohjelmointia: list comprehension -rakenne

Syntaksi lukusarjojen tuottamiseen alla on lyhyt syntaksi yksinkertaisten äärellisten tai äärettömien lukusarjojen tekemiseen toteutetaan muuttamalla syntaksi funktiokutsuksi: esim. SICP-kirjan (enumerate-interval 3 6) tämän kaltainen syntaksi on käytössä joissain ohjelmointikielissä ja matematiikkaohjelmistoissa, esim. Haskell, Matlab, Octave Pythonissa on vain range- ja xrange-funktiot joskus syntaksia voi käyttää numeroiden lisäksi esim. enumeraatioiden kanssa Haskell-esimerkkejä [1..] [1,2,3,4,...] [3..6] [3,4,5,6] [5,10..30] [5,10,15,20,25,30]

List comprehension -syntaksi kätevä tapa koota listoja tietyt ehdot täyttävistä alkioista tai käydä joukko vaihtoehtoja läpi antaa lyhyemmän syntaksin map:n ja filter:n yhdistelmille syntaksi on lainattu matematiikasta: esim. joukko {x y x {1, 2,..., 10}, y {1, 2,..., 10}, x < y} list comprehension -syntaksi muutamassa kielessä: Haskell: [x*y x <- [1..10], y <- [1..10], x<y] Python: [x*y for x in range(1,11) for y in range(1,11) if x<y] Scala: for (x <- List.range(1,10); y <- List.range(1,10) if x < y) yield x*y Pythonissa on lisäksi iterator comprehension (muuten sama mutta tekee iteraattoreita eikä listoja): (x*y for x in range(1,11) for y in range(1,11) if x<y) ehkä nämä tulevat vähitellen muihinkin kieliin, joissa on luontevaa tuottaa listoja tai iteraattoreita?

List comprehensionin toteuttamisesta list comprehension luo kaikki annettujen arvojen yhdistelmät, vasemmalta oikealle, ja poistaa ne, jotka eivät täytä ehtoja Esimerkki list comprehensionin aukikirjoittamisesta [x*y for x in range(1,11) for y in range(1,11) if x<y] ;; Pythonista Schemeksi muunnettuna: (flatmap (lambda (x) (map (lambda (y) (* x y)) (filter (lambda (y) (< x y)) (enumerate-interval 1 10)))) (enumerate-interval 1 10)) Muutama lisäesimerkki Haskell-kielellä [ x x <- [1..9], even x ] [2,4,6,8] [ (x,y) x <- [1..4], y <- [1..4], x < y ] [(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)] factors n = [ i i <- [1..n-1], n `mod` i == 0 ] ;; Viimeinen Schemellä: (define (factors n) (filter (lambda (i) (= (remainder n i) 0)) (enumerate-interval 1 (- n 1))))