Upload
jouni-smed
View
461
Download
9
Tags:
Embed Size (px)
DESCRIPTION
Kurssilla tarkastellaan ohjelmointikieliä ja paradigmoja teoreettiselta, tekniseltä ja historialliselta kannalta. Aihetta konkretisoidaan tutustumalla eri ohjelmointiparadigmoja edustaviin ohjelmointikieliin. Kurssi luo kokonaiskuvan ohjelmointikielten kehitykseen, minkä jälkeen tarkastellaan lähemmin eri ohjelmointiparadigmoja. Imperatiivisen ohjelmoinnin osalta luodaan kokonaiskuva ohjelmarakenteisiin ja olio-ohjelmoinnin osalta syvennetään ymmärrystä periytymismekanismin ja geneerisyyden toiminnasta. Deklaratiivinen ohjelmoinnin osalta keskitytään erityisesti funktionaaliseen ohjelmointiin ja sen teoreettiseen taustaan (mm. lambda-kalkyyliin) sekä logiikkaohjelmointiin.
Citation preview
Jouni Smed Kevät 2014
Yleistä
� Laajuus: 5 op. � Esitiedot (suositus):
� Olio-ohjelmoinnin metodiikka (tai Sopimuspohjainen olio-ohjelmointi)
� Kotisivu: bit.ly/okp2014
Opetusajat
� Luennot: tiistaisin 21.1–15.4.2014 klo 12–14 salissa β � ei 3. periodin viimeisellä viikolla 4.3
� Demonstraatiot: torstaisin 6.2–27.2.2014 salissa B2039 � ryhmät: 8–10, 10–12, 12–14 ja 14–16
Kurssin arvostelu � Arvostelu
perustuu 100 pisteeseen
� Pistejako � tentti: 60 pistettä � demonstraatiot:
20 pistettä � harjoitustyö: 20
pistettä
� Hyväksytty kurssi vaatii yhteensä 45 pistettä
� Arvosanajako � [45,55) ⇒ 1 � [55, 65) ⇒ 2 � [65, 75) ⇒ 3 � [75, 85) ⇒ 4 � [85, 100] ⇒ 5
Harjoitustyö
� Tehdään 4. periodissa � Arvostelu
� hylätty (0 pistettä) � välttävä (5 pistettä) � tyydyttävä (10 pistettä) � hyvä (15 pistettä) � kiitettävä (20 pistettä)
� Lisätietoa muodosta ja suoritustavasta helmikuun loppupuolella
Tentit
� Sähköinen tentti � avautuu 5.5.2014 � sulkeutuu 30.9.2014
� Tentin voi suorittaa enintään kolme (3) kertaa
� Lisäohjeita ja ajanvaraus: https://tenttis.utu.fi
Kurssikirjat
� Smed, Hakonen, Raita: Sopimuspohjainen olio-ohjelmointi Java-kielellä, 2007. ISBN 978-952-92-1776-2 � luvut 5–7 ja 9
� Scott: Programming Language Pragmatics, 3rd edition, 2009. ISBN 978-0-12-374514-9 � luvut 9–13
Kurssiaikataulu (3. periodi) Kerta Pvm Aihe
1. 21.1 Ohjelmointiparadigmat
2. 28.1 Olio-ohjelmointi: periytyminen
3. 4.2 Olio-ohjelmointi: periytyminen
(i) 6.2 1. demonstraatiot
4. 11.2 Olion perusoperaatiot ja elinkaari
(ii) 13.2 2. demonstraatiot
5. 18.2 Geneerisyys
(iii) 20.2 3. demonstraatiot
6. 25.2 Olio-ohjelmoinnin erityispiirteitä
(iv) 27.2 4. demonstraatiot
Kurssiaikataulu (4. periodi) Kerta Pvm Aihe
7. 11.3 Funktionaalinen ohjelmointi
8. 18.3 Funktionaalinen ohjelmointi
9. 25.3 Logiikkaohjelmointi
10. 1.4 Rinnakkaisohjelmointi
11. 8.4 Rinnakkaisohjelmointi
12. 15.4 Muita ohjelmointiparadigmoja
Kertaus: Kurssin suorittaminen
� Tentti � sähköinen tentti 5.5–30.9.2014 � max 60 pistettä
� Demonstraatiot � neljä kertaa 3. periodissa � max 20 pistettä
� Harjoitustyö � suoritetaan 4. periodissa � max 20 pistettä
The Jargon File: Programming programming: n. 1. The art of debugging a blank sheet of paper (or,
in these days of on-line editing, the art of debugging an empty file). “Bloody instructions which, being taught, return to plague their inventor” (Macbeth, Act 1, Scene 7)
2. A pastime similar to banging one's head against a wall, but with fewer opportunities for reward.
3. The most fun you can have with your clothes on.
4. The least fun you can have with your clothes off.
Mitä on ohjelmointi?
� Luodaan toimintaohjeet (eli lähdekoodi) joilla tietokone voi suorittaa tietyn tehtävän tai käyttäytyä tietyllä tavalla
Ohjelmointikielten sukupolvet
1. Konekielet 2. Symboliset konekielet (eli assembly-
kielet) 3. Korkean tason ohjelmointikielet 4. Erittäin korkean tason kielet ja
kehitysympäristöt 5. Rajoite- ja logiikkaohjelmointikielet
1. sukupolvi: Konekielet
� Tietokoneen keskusyksikön suorittamia käskyjä � sekvenssi nollia ja ykkösiä
� Ohjelmointi tapahtui syöttämällä konekäskyjä binäärimuodossa suoraan paneelin kautta
0110101
2. sukupolvi: Symboliset konekielet (eli assembly-kielet) � Ohjelmoijan kirjoitettavissa ja luettavissa
� tekstuaalinen esitystapa konekielelle � makrot
� Ohjelma käännetään konekielelle � Riippuu alustasta
� eri prosessoreilla eri konekielet
lda #$FF eor $C011,X bne LOOP
3. sukupolvi: Korkean tason ohjelmointikielet � 1950-luvun lopulla: Fortran, ALGOL,
COBOL � ihmiskielen kaltaisia � syntaksi
� Yhä edelleen suosittuja: C, C#, Java, BASIC…
� Käännetään konekielelle tai ajetaan tulkin kautta � eivät riipu alustasta VAR Ch: char
BEGIN WHILE NOT eoln DO
read(Ch) IF Ch =‘a’ THEN
Res := 0; ELSE
4. sukupolvi: Erittäin korkean tason kielet ja kehitysympäristöt � Kooditon ohjelmointi
� raporttigeneraattorikielet � CASE-työkalut � tiedonhallintatyökalut
5. sukupolvi: Rajoite- ja logiikkaohjelmointikielet � Ongelma kuvataan rajoitteilla � Deklaratiivinen ohjelmointi � Tietokone ratkaisee ongelman ilman
ohjelmoijaa
min wk’Σwk s.t. wk’ µ = rk
wki = 1 0 ≤ wk≤ 1
Ohjelmointiparadigma
� Ohjelmointikielen taustalla oleva perustavanlaatuinen ajattelutapa � millaisista osista ohjelma rakentuu? � miten ohjelman suoritus etenee?
� Ohjelmointikieli voi tukea useaa paradigmaa
Pääparadigmat
Imperatiivinen ohjelmointi
“Miten päästään
päämäärään”
Deklaratiivinen ohjelmointi
“Mikä on
päämäärä”
Imperatiivinen ohjelmointi 1(2)
� Taustalla Turingin kone ja von Neumannin konemalli � konekielet ovat imperatiivisia
� Laskenta kuvataan lausekkeina jotka muuttavat ohjelman tilaa � ts. käsketään konetta tekemään mitä
ohjelmoija haluaa
Imperatiivinen ohjelmointi 2(2)
� Proseduraalinen ohjelmointi � aliohjelmat ja funktiot
� Rakenteellinen (structured) ohjelmointi � tilan muutokset rajataan aliohjelmien sisälle,
niiden parametreihin ja paluuarvoihin � Modulaarinen ohjelmointi
� ohjelman toiminnallisuus rajataan riippumattomiin, vaihdettaviin moduleihin
� Oliokeskeinen ohjelmointi � tiedon kapselointi, periytyminen ja dynaaminen
metodin sidonta
Deklaratiivinen ohjelmointi 1(2)
� Taustalla jokin formaali järjestelmä � laskenta on dedusointia ko. järjestelmässä
� Sivuvaikutusten eliminointi (tai ainakin minimointi) � parantaa ohjelmiston luotettavuutta � helpottaa rinnakkaislaskentaa
Deklaratiivinen ohjelmointi 2(2)
� Kyselykielet � esim. tietokannat
� Säännölliset ilmaukset � Funktionaalinen ohjelmointi
� taustalla lambda-kalkyyli � Logiikkaohjelmointi
� taustalla ensimmäisen asteen logiikka
Esimerkki: Kakun leipominen…
Imperatiivisesti Deklaratiivisesti
� Lämmitä uuni 175°C:een. � Pehmitä 250 g voita
mikroaaltouunissa. � Vaahdota voi ja 250 g sokeria
vaaleaksi vaahdoksi. � Lisää 4 kananmunaa pienissä
erissä koko ajan sekoittaen. � Yhdistä 5 g leivinjauhetta ja
250 g jauhoja ja lisää ne vaahtoon.
� Sekoita kunnolla, mutta ei liian kovalla teholla.
� Kaada seos kakkuvuokaan. � Paista uunissa 60 minuuttia.
Aikajana: 1950- ja 60-luvut � FORTRAN
� imperatiivinen (myöh. proseduraalinen) � asetuslauseet, ehtolauseet, silmukat, I/O (myöh. aliohjelmat)
� LISP � funktionaalinen � taustalla lambda-kalkyyli
� ALGOL � proseduraalinen, rakenteellinen � vaikuttanut suuresti myöhempiin kieliin
� COBOL � proseduraalinen � suunnattu ei-ohjelmoijille
� Simula � oliokeskeinen � esitteli oliot, luokat, aliluokat, virtuaaliset metodit, korutiinit, diskreetin
tapahtumasimuloinnin ja roskienkeruun � Muita kieliä: APL, BASIC, LOGO
Algol elää! begin integer N; Read Int(N); begin real array Data[1:N]; real sum, avg; integer i; sum := 0; for i := 1 step 1 until N do begin real val; Read Real(val); Data[i] := if val < 0 then -val else val end; for i := 1 step 1 until N do sum := sum Data[i]; avg := sum / N; Print Real(avg) end end
Aikajana: 1970-luku � Pascal
� rakenteellinen � tarkoituksena opettaa hyviä ohjelmointikäytäntöjä
� C � proseduraalinen, rakenteelinen � kiinnittyy alla olevaan konekieleen ja laitteistoon � laajimmalle levinnein ohjelmointikieli
� Smalltalk � oliokeskeinen � kehitetty opetuskäyttöön � toi olio-ohjelmoinnin yleiseen tietoon ja vaikutti muiden oliokielten
kehitykseen � Prolog
� logiikkaohjelmointi � ohjelmat muodostuvat säännöistä ja kyselyistä
� Muita kieliä: Forth, SQL, ML, CLU, Scheme, Modula, AWK
Aikajana: 1980-luku � C++
� proseduraalinen, oliokeskeinen (myöh. funktionaalinen) � alunperin laajennus C-kieleen: luokat, virtuaaliset funktiot,
ylikuormitus, moniperintä, poikkeukset � yhä yksi suosituimmista ohjelmointikielistä
� Ada � modulaarinen (myöh. oliokeskeinen) � alunperin sulautettuihin ja reaaliaikaisiin sovelluksiin sekä
suuriin tietojärjestelmiin � Perl
� komentosarja, rakenteelinen (myöh. oliokeskeinen) � vaikutteita monista kielistä: suuri ilmaisunvapaus
� Muita kieliä: PostScript, occam, Eiffel, Objective-C, Erlang
Aikajana: 1990-luku � Java
� rakenteellinen, oliokeskeinen � alustariippumaton: ohjelma ajetaan tavukoodina
virtuaalikoneessa � yksi suosituimmista ohjelmointikielistä
� JavaScript � komentosarja, rakentellinen, oliokeskeinen
(prototyypit), funktionaalinen � Ruby
� oliokeskeinen, funktionaalinen � Muita kieliä: Haskell, Python, Lua
Aikajana: 2000-luku
� C# � rakenteellinen, olio-keskeinen (myöh.
funktionaalinen) � Scala
� olio-keskeinen, funktionaalinen � Muita kieliä: ActionScript, AspectJ, Io,
Clojure
Miksi tutustua erilaisiin ohjelmointikieliin? � Ajatusten esitttämiskyky laajenee � Riittävät taustatiedot sopivan
ohjelmointikielen valintaan � Kyky omaksua uusia ohjelmointikieliä � Kielen toteutuksen merkityksen
ymmärtäminen � Kyky seurata, ymmärtää ja arvioida
ohjelmointikielten kehitystä
Kuinka ohjelmointikielten käyttäjät näkevät toisensa…
Aiheita
� Periytyminen � Olion perustoimenpiteet ja elinkaari � Geneerisyys � Asiakas- ja periytymisrelaatiot � Aspektit
Periytyminen
Abstrakti tietotyyppi
� Abstrakti tietotyyppi (abstract data type, ADT) kuvaa � tietoalkion rakenteen � sille suoritettavat operaatiot
� ADT antaa tyypille julkisen liitännän muttei toteutusta
� Luokka eroaa ADT:stä siinä, että se voi antaa operaatioille myös toteutuksen
Esimerkki: ADT Pino
Esimerkki: ADT Jono
Periytyminen
� Olio-ohjelmoinnin keskeinen ajatus on luokkien muodostama hierarkia � luokat eivät ole täysin erillisiä kuten rutiinit
proseduraalisessa ohjelmoinnissa � Esim: työntekijä on henkilö, jolla on
jotain erityisominaisuuksia, joita ei ole kaikilla henkilöilla
Polymorfismi
� Tunnisten kyky viitata moneen erityyppiseen olioon
Henkilö yksilö;
Työntekijä duunari;
duunari = new Työntekijä("B. Virtanen");
yksilö = duunari;
Työntekijä
Henkilö
Alityypitys ja periytyminen
� Liskovin korvausperiaate: alityypit käyttäytyvät siten kuin niiden ylityyppien määrittelyt ilmaisevat
� Oikeaoppinen tyyppiteorian käyttö � periytyjä on perimänsä luokan alityyppi � periytyjä noudattaa perimänsä luokan
operaatioiden määrittelyjä
Korvausperiaateen seurauksia
� Yli- ja aliluokan suhde ei ole symmetrinen � yliluokka ei voi tietää, mitkä luokat periytyvät (tai
tulevat periytymään) siitä ja mitä piirteitä ne ovat muokanneet
� siis Henkilö ei voi korvata Työntekijää
� Yliluokan tyyppiä voidaan kutsua tietämättä, että kutsutun operaation toteutus on aliluokassa � operaation kutsu on siis erotettu suoritettavasta
operaatiosta
Staattinen ja dynaaminen tyyppi 1(3) � Staattinen tyyppi
� ilmaistaan muuttujan esittelyssä � pysyy muuttumattomana
� Dynaaminen tyyppi � määräytyy sen mukaan minkälaiseen olioon
muuttuja kullakin hetkellä viittaa
Staattinen ja dynaaminen tyyppi 2(3) Henkilö yksilö = new Työntekijä("Lennart Nilkén");
Työntekijä duunari = new Työntekijä("B. Virtanen");
� Dynaaminen tyyppijoukko � staattisen tyypin ja sen alityyppien
muodostama joukko � sopivat yhteeen staattisen tyypin kanssa
Staattinen ja dynaaminen tyyppi 3(3) � Mahdottomia asetuksia kun Henkilö yksilö ja Työntekijä duunari � duunari = yksilö; � yksilö = duunari; ansio = yksilö.annaPalkka();
� Yleistulkinta (upcasting) � yksilö = duunari;
� Erikoistulkinta (downcasting) � duunari = (Työntekijä)yksilö;
Sidonta
� Luokka A ja sen perijä B antavat eri toteutuksen rutiinille f,
� Luokan A asiakas kutsuu rutiinia käyttäen muuttujaa x, johon liittyy polymorfisesti luokan B olio � ts. A x = new B(); x.f();
� Mitä tapahtuu?
Vaihtoehto 1: Staattinen sidonta
� Valintapäätös tehdään käännösaikana � kääntäjä ei voi tietää muuttujan x dynaamista
tyyppiä � x.f() kutsuu luokan A rutiinia
� Oletus mm. C++:ssa (ohjelmoijan muutettavissa: virtuaaliset funktiot)
� Javassa staattisesti sidotaan � jäsenmuuttujat � static-luokkametodit � final-rutiinit � private-rutiinit
Vaihtoehto 2: Dynaaminen sidonta � Valintapäätös tehdään ajoaikana
� rutiini f valitaan muuttujaan x sillä hetkellä liitetyn olion tyypin mukaan
� x.f() kutsuu luokan B rutiinia � Oletussidonta Javassa
� ei ohjelmoijan muutettavissa
C++:n virtuaaliset funktiot
� Oletuksena C++:ssa metodit sidotaan staattisesti
� Mikäli metodi määritetään virtuaaliseksi, se sidotaan dynaamisesti ajoaikana
Esimerkki: Virtuaaliset funktiot class Perus { public: virtual char f() { return 'P'; } char g() { return 'P'; } char testaaF() { return f(); } char testaaG() { return g(); } }; class Johdettu: public Perus { public: virtual char f() { return 'J'; } char g() { return 'J'; } }; main () { Johdettu j; print j.testaaF(); print j.testaaG(); }
Rutiinin korvaus ja ylikuormitus � On mahdollista että aliluokassa määritellään
samanniminen metodi kuin yliluokassa � Aliluokka voi korvata (override) yliluokan metodin
� kutsuparametrit samanlaiset kuin yliluokassa � yliluokan metodia ei enää käytetä aliluokassa tai sen
jälkeläisissä � Aliluokka voi ylikuormittaa (overload) metodin
� kutsuparametrit erilaiset � kyse uudesta metodista, joka ei korvaa vanhaa
� Milloin on kyse korvauksesta, milloin ylikuormituksesta? � kolme vaihtoehtoista sääntöä: kovarianssi, kontravarianssi
ja novarianssi
Kovarianssi � Kyse on rutiinin korvauksesta, jos
aliluokka muuttaa yliluokan rutiinin argumenttityypin, tulostyypin ja/tai poikkeustyypin alityypikseen
� Esimerkki: jos yliluokassa määritellään public Number annaNumero()
ja sen aliluokassa public Integer annaNumero()
kyse on korvauksesta
Kontravarianssi � Kyse on korvauksesta, jos alityyppi muuttaa rutiinin
argumenttityypin, tulostyypin ja/tai poikkeustyypin ylityypikseen
� Esimerkki:
class Kädellinen extends Nisäkäs { void vertaa(Compable<? super Kädellinen> k {...} } class Apina extends Kädellinen { void vertaa(Compable<? super Nisäkäs> k {...} }
Novarianssi
� Kyse on korvauksesta, jos tyypit ovat täsmälleen samat
Korvaus � Korvauksessa aliluokan toteutus korvaa
yliluokan toteutuksen � Javassa rutiinin nimen ja argumenttien
tulee olla samat ja palautustyypin ja poikkeusten tulee noudattaa kovarianssisääntöä
� Korvauksessa suojausmäärettä saa muuttaa vain väljemmäksi � muutenhan aliluokka voisi muuttaa jonkin
yliluokan julkisen piirteen privaatiksi…
Ylikuormitus
� Ylikuormituksessa yliluokan metodi ei korvaudu, vaan se on yhä edelleen käytettävissä
� Uusi, samanniminen metodi erotetaan erilaisten argumenttiensa avulla
Lyhyesti: Rutiinien korvaus ja ylikuormitus
Ta paluutyyppi nimi (argumentit) poikkeukset
Tb paluutyyppi nimi (argumentit) poikkeukset
Ta paluutyyppi nimi (argumentit) poikkeukset
Tb paluutyyppi nimi (argumentit) poikkeukset
Luokkatyypit
� Rajapintaluokka (interface) � Abstrakti luokka (abstract class) � Konkreetti luokka (concrete class)
Rajapintaluokka
� Luokkamäärittely: interface � Ei sisällä rutiinitoteutuksia � Kaikki piirteet ovat tyypiltään
� public � abstract
� Määrittelee roolin toteuttajille
Abstrakti luokka
� Luokkamäärittely: abstract class � Voi sisältää sekä abstrakteja että
konkreetteja rutiineja � Ei voida konstruoida! � Voi silti toteuttaa konstruktorin
periytymistä varten
C++: Puhtaat virtuaaliset metodit class Puskuri { public: virtual int lisaa(char x); virtual char poista(); }; class Jono : public Puskuri { public: Jono() { koko = MAX + 1; etu = taka = 0; } int lisaa(char) { /* toteutus */ } char poista() { /* toteutus */ } private: char sisalto[MAX + 1]; int koko, etu, taka; int seur(int i) { return (i + 1) % koko; } };
Konkreetti luokka
� Luokkamäärittely: class � Kaikki piirteet on määritelty � Voidaan konstruoida
Miksi abstrahoida? � Abstrakteista yliluokista on hyvin vähän tai
ei ollenkaan viittauksia muutosalttiisiin konkreetteihin luokkiin
� Muut luokat voivat viitata konkreettien luokkien sijaan näiden abstrakteihin yliluokkiin ja dynaaminen sidonta huolehtii kulloisenkin konkreettisen rutiinin löytymisestä
� Muutokset konkreettiin toteutukseen eivät vaikuta korkeimman tason luokkiin
� Abstraktit luokat muodostavat ohjelmiston ytimen, johon koko muu ohjelmisto nojaa
Luokat pakkauksessa: abstraktisuus–epävakaus
A
I
(0,1) (1,1)
(1,0) (0,0)
Periytyminen
� Javassa on käytössä yksittäisperiytyminen � luokka periytyy yhdestä konkreetista tai
abstraktista luokasta � luokka voi toteuttaa useita rajapintaluokkia
� Mikäli luokka määritellään lopulliseksi (final), siitä ei voi periytyä � mikäli jokin luokan piirre määritellään
lopulliseksi, periytyjä ei saa muuttaa sitä
Luokkahierarkia � Javassa kaikki luokat periytyvät Object-luokasta � jokaisen olion julkiseen liitäntään kuuluu siis
joukko Object-luokasta periytyviä piirteitä kuten equals, hashCode, toString
� Yliluokasta periytyy kaikki paitsi private-suojausmääreellä varustetut piirteet � periytymisessä suojausmääreitä voi
ainoastaan väljentää
Javan suojausmääreet
� public � package � protected � private
Suojausmääreiden vaikutusalueet Javassa
suojaus aliluokka samassa pakkauksessa
aliluokka eri pakkauksessa
ei-periytyvä luokka samassa pakkauksessa
ei-periytyvä luokka eri pakkauksessa
private – – – –
package voi käyttää – voi käyttää –
protected voi käyttää voi käyttää voi käyttää –
public voi käyttää voi käyttää voi käyttää voi käyttää
C++:n suojausmääreet
� piirteen suojausmääre � public � protected � private
� periytymisen suojausmääre � public � protected � private
Suojausmääreiden vaikutusalueet C++:ssa
suojaus periytymis-tapa
aliluokka aliluokan asiakas
aliluokan metodi
aliluokan perillinen
private – ei voi käyttää
– – –
protected private private – voi käyttää –
protected protected – voi käyttää voi käyttää
public protected – voi käyttää voi käyttää
public private private – voi käyttää –
protected protected – voi käyttää voi käyttää
public public voi käyttää voi käyttää voi käyttää
Moniperiytyminen
� Moniperiytymisen sallivia kieliä � C++, Eiffel, Python
� Yksittäisperitymisen sallivia kieliä � Simula, Smalltalk, Objective-C
� “Sekaperitymisen” sallivia kieliä � Java, C#, Ruby
Moniperiytymisen ongelmia � Jos kaksi yliluokkaa tarjoaa samannimisen
metodin, kumpaa käytetään aliluokassa? � voidaanko kutsua vain toista vai kumpaakin?
� Jos yliluokilla on yhteinen yliluokka, kuinka monta kertaa se toistuu aliluokassa? � C++ (oletus): toistettu periytyminen eli useita
kopioita yliyliluokasta � Eiffel (oletus): jaettu periytyminen eli yksi kopio
yliyliluokasta
C++: Moniperiytyminen � Oletus: D sisältää kaksi
erillistä A-oliota ja A:n jäsenet täytyy kvalifioida
� Jos B:n ja C:n periytymiset on merkittyy virtuaalisiksi (class B : virtual public A), A:sta on yksi olio
� Jos virtuaalinen ja ei-virtuaalinen periytyminen on sekoitettu, on yksi virtuaalinen A ja yksi ei-virtuaalinen A
� Yliluokan piirre täytyy kvalifioida B::A.f()
Käyttö- ja toteutussopimus 1(2)
� Luokalla on sopimuksia kahteen suuntaan � käyttösopimus: luokka takaa asiakkailleen
tietyt palvelut (so. julkinen liitäntä) � toteutussopimus: luokka takaa aliluokilleen
tietyt palvelut � Aliluokan on noudatettava yliluokan
käyttösopimusta � alkuehtoja voi heikentää ja loppuehtoa
vahventaa
Käyttö- ja toteutussopimus 2(2)
Asiakas
Toteuttaja
public
sisäinen toteutus
private
käyttösopimus
Uudelleen- käyttäjä
protected, public
toteutus- sopimus
Esimerkki: Laskuri 1(2) /** @.classInvariantProtected laskuri on aina * parillinen */ public class ParillinenLaskuri { protected int laskuri; public ParillinenLaskuri() { laskuri = 0; } public int annaLaskuriArvo() { return laskuri; } public void kasvataKahdella() { laskuri += 2; } }
Esimerkki: Laskuri 2(2) public class YleinenLaskuri
extends ParillinenLaskuri {
public YleinenLaskuri() {
laskuri = 0;
}
public void kasvataYhdellä() {
laskuri++;
} }
Periytymisen käyttö � Erikoistaminen
� esitetään käsitehierarkia luokkahierarkiana � ylätason käsite voidaan jakaa useampaan kuin yhteen alatason luokkaan
� Tarkentaminen � alatason käsite on sama kuin ylätason käsite � kiinnitetään toteuttamatta jätetyt piirteet alatasolla
� Yhdistäminen � yhdistetään monta eri roolia � tarkennettu käsite voi esiintyä useammissa rooleissa kuin yliluokka
� Toteutusperiytyminen � yläluokalla ei välttämättä ole suoraa suhdetta aliluokkaan � tarkoituksena välttää samojen operaatioiden kirjoittamista uudelleen,
käyttää niitä työkaluina � Ad hoc -periytyminen
� ei luoda laajempaa hierarkiaa, vaan ratkaistaan eteen tullut ongelma periytymisellä, jos se vaatii vähiten vaivaa
Olion perustoiminnot ja elinkaari
Olion perustoiminnot
� Alustus � Samuus ja vertailu � Kloonaus � Viimeistely � Vapautus
Olion perustoiminnot Javassa � Kaikki luokat periytyvät Object-luokasta,
jonka julkinen liitäntä määrittelee joukon metodeja � liittyvät abstrakteihin toimintoihin, jotka voivat
kohdata mitä tahansa oliota, esim. kopiointi, säikeistys, merkkijonoesitys, samuus…
� Kanoninen olio antaa metodeille järkevät toteutukset
� Perustoimintoihin kuuluu myös alustusoperaatio
Alustus 1(2) � Konstruktorin nimi on tarkalleen sama
kuin luokan nimi � Ei palautusarvoa (ei edes void) � Kutsu vain new-operaattorilla � Ei periydy (!)
� perivän luokan täytyy määritellä konstruktori(t) uudestaan
� Voi kutsua yliluokan konstruktoria � super(parametrit); � täytyy olla konstruktorin ensimmäinen
operaatio
Alustus 2(2) � Oletuksena sama suojausmääre kuin
luokalla � Voidaan ylikuormittaa
� voi kutsua toista konstruktoria: this(parametrit);
� Mikäli käyttäjä ei ole määritellyt yhtään konstuktoria, systeemi luo oletuskonstruktorin � ei parametreja � alustaa jäsenmuuttujat oletusarvoihin
Esimerkki: konstruktorin ylikuormitus class Neliö { Neliö(int x, int y) { this(x, y, 10, Color.BLACK); } Neliö(int x, int y, int koko { this(x, y, koko, Color.BLACK); } Neliö(int x, int y, int koko, Color väri) { this.x = x; this.y = y; this.koko = koko; this.väri = väri; } }
C++:n konstruktori
� Voidaan ylikuormittaa ja sen parametreille voidaan antaa oletusarvoja
� Ei palauta mitään arvoa (ts. sille ei määritellä paluuarvoa)
Esimerkki: C++:n konstruktorit class Pvm { public: Pvm(int, int, int); Pvm(int, int); Pvm(int); Pvm(const char*); Pvm(); // oletuskonstruktori private: int pp_, kk_, vv_; }; Pvm jokuPaiva(11); Pvm uusiVuosi("1. tammikuuta 2013"); Pvm tanaan;
C++:n erikoiskonstruktorit � Oletuskonstruktori
� parametriton � luodaan implisiittisesti jos luokalle ei ole
määritelty konstruktoreita � ei luoda, jos konstruktoreita on määritelty
� Kopiokonstruktori � käytetään olioiden kopiointiin � saa parametriksi viittauksen luokan olioon � luodaan implisiittisesti jos käyttäjä ei ole
määritellyt uuttta toteutusta
Olioiden samuus � Identtisyys
� ehto a == b on tosi � Pintasamuus
� olioiden a ja b jäsenmuuttujat sisältävät identtiset arvot
� Syväsamuus � oliot a ja b ovat pintasamat � kaikki olioiden a ja b viittaustyyppisistä
vastinjäsenmuuttujista on rekursiivisesti pintasamat
Samanlaisuuden asteet
equals-operaation toteutusvaatimukset i. Refleksiivisyys: x.equals(x) ii. Symmetrisyys: x.equals(y) <==> y.equals(x)
iii. Transitiivisuus: x.equals(y) & y.equals(z) ==> x.equals(z)
iv. Konsistenssi: Jos pintasamuuteen liittyviä tietoja ei muuteta, vertailun on palautettava johdonmukaisesti tosi tai epätosi
v. Null-epäsamuus: x.equals(null) == false vi. Rutiinin on toimittava olioiden tyypistä huolimatta.
Esimerkki: Henkilö public boolean equals(Object toinen) {
if (toinen == null) return false; if (toinen == this) return true;
if (!(toinen instanceof Henkilö)) return false;
Henkilö kaveri = (Henkilö)toinen;
return nimi.equals(kaveri.nimi) &
osoite.equals(kaveri.osoite) & syntymävuosi == kaveri.syntymävuosi;
}
Comparable ja Comparator 1(2)
� Comparable-rajapinnan määrittelemä metodi compareTo määrittää oliolle luonnollisen järjestyksen � toinen vertailtava on sen rajapinnan toteuttava
luokka itse � Comparator-rajapinnan toteuttava luokka
toimii erillisenä vertailijana � vertailee kahta itsestään erillistä oliota � voidaan toteuttaa monta erilaista vertailuoliota,
jotka voivat antaa samoille olioille erilaisia järjestyksiä eri kriteereitten mukaan
Comparable ja Comparator 2(2) � Vertaillaan kahta samantyyppistä oliota keskenään ja
palautetaan tieto suuruusjärjestyksestä: a.compareTo(b) tai compare(a, b) � negatiivinen kokonaisluku, mikäli a on pienempi kuin b � nolla, mikäli yhtäsuuret � positiivinen kokonaisluku, mikäli a on suurempi kuin b � ClassCastException, mikäli a ja b ovat eri tyyppia
� Huomaa! a.compareTo(b)==0) <==> (a.equals(b)) � metodeita voidaan käyttää samaan tarkoitukseen
Esimerkki: Comparable-rajapinta public class Henkilö implements Comparable<Henkilö> {
/* ... */
public int compareTo(Henkilö toinen) {
int tulos = this.nimi.compareTo(toinen.nimi);
if (tulos != 0) return tulos; if (this.syntymävuosi != toinen.syntymävuosi)
return (this.syntymävuosi <
toinen.syntymävuosi) ? -1 : 1;
else return
this.osoite.compareTo(toinen.osoite);
}
}
Kloonaus 1(2) � Kloonauksessa oliosta tehdään täysin erillinen
kopio � ei ole jaettuja olioita millään tasolla � kopioon tehdyt muutokset eivät vaikuta
alkuperäiseen olion (ja päinvastoin) � Mikäli olion jäsenmuuttujat ovat
primitiivityyppejä tai viittauksia mutatoitumattomiin olioihin, riittää pintakopio � kopioidaan alkuperäisen olion jäsenmuuttujat ja
olioviittausten muistiosoite � Syväkopiossa kopiointioperaatio lähetetään
rekursiivisesti eteenpäin oliossa oleviin olioviittauksiin
Kloonaus 2(2) � Kloonaus tehdään clone-metodilla
� vaatii, että luokka toteuttaa Cloneable-rajapinnan � metodin tulee kutsua yliluokansa clone-metodia
(super.clone) kunnes tullaan Object-luokan toteutukseen, joka tekee oliosta bitittäisen kopion
� metodi täydentää osaltaan yliluokasta saatua kopiota ja palauttaa sen kutsujalleen
� mikäli luokka luokka ei toteuta Cloneable-rajapintaa, metodi heittää CloneNotSupported-poikkeuksen
� Huomaa! Jos luokka toteuttaa Cloneable-rajapinnan, kaikki siitä periytyvät luokat ovat myös kloonattavissa
Esimerkki: Cloneable-rajapinta public class JangoFett implements Cloneable {
public JangoFett clone() {
try {
return (JangoFett) super.clone();
} catch (CloneNotSupportedException e) { throw new InternalError(e.toString());
}
}
Olioiden kopiointi C++:ssa
� Sijoitusoperaattori (=) � oletuksena pintakopio
� Kopiokonstruktori � oletuksena pintakopio
Esimerkki: kopiointi C++:ssa class Kopioitava { public: Kopioitava() { // Oletuskonstruktori } Kopioitava(const Kopioitava& x) { // Kopiokonstruktori } Kopioitava& operator=(const Kopioitava& x) { // Sijoitus } };
Roskien keruu
� Kun olioon ei ole viittauksia, roskien keruu (garbage collection) voi poistaa sen � ei viittauksia elävistä säikeistä � ei staattisia viitauksia � sykliset viittaukset eivät vaikuta
� Ennen poistoa olio viimeistellään (finalize)
Viimeistely
� Metodi finalize mahdollistaa olion varaamien resurssien vapauttamisen kun olio tuhotaan � käytännössä tarvitsee toteuttaa erittäin
harvoin
Esimerkki: Viimeistely protected void finalize() throws Throwable {
try {
suljeKaikki();
} finally {
super.finalize(); }
}
C++:n destruktori � Kutsutaan kun olio tuhotaan � Nimi alkaa tilde-merkillä (~) jota seuraa
luokan nimi � Ei palauta arvoa � Oletusdestruktori luodaan implisiittisesti
mikäli luokalle ei ole annettu destruktoria � oletustoteutus: vapauttaa jäsenmuuttujille
varatun muistin � Tyypillinen käyttötapa on jonkin resurssin
varaaminen ja vapauttaminen
Esimerkki: destruktori C++:ssa class Esimerkki { public: Esimerkki(int n = 10) { n_ = n; taulukko_ = new int[n]; } ~Esimerkki() { delete[] taulukko_; } private: int* taulukko_; int n_; };
Geneerisyys
Geneerinen ohjelmointi
� Ohjelmointia jossa tyypit määritellään myöhemmin instantioitaessa parametreina
� Esiteltiin 1970-luvulla ML-kielessä � myös CLU ja Ada
� Tullut osaksi myös oliokeskeisiä kieliä � Java: geneeriset luokat ja metodit � C++: mallit (templates)
Ada: Geneerinen proseduuri generic type Alkio_T is private; procedure Vaihda (X, Y : in out Alkio_T); procedure Vaihda (X, Y : in out Alkio_T) is Apu : constant Alkio_T := X; begin X := Y; Y := Apu; end Vaihda; procedure Vaihda_Kokonaisluvut is new Vaihda(Integer); procedure Vaihda_Liukuluvut is new Vaihda(Float);
Ada: Geneerinen pakkaus 1(2) generic Max: Positive; type Alkio_T is private; package Yleinen_Pino is procedure Lisaa(A: Alkio_T); function Poista return Alkio_T; end Yleinen_Pino; package body Yleinen_Pino is Pino: array (1 .. Max) of Alkio_T; Ylin: Integer range 0 .. Max := 0; -- jne. jne. end Yleinen_Pino;
Ada: Geneerinen pakkaus 2(2)
declare
package Liukuluku_250_Pino is
new Yleinen_Pino (250, Float);
use Liukuluku_250_Pino;
begin
Lisaa (2.12);
-- jne. jne.
end;
C++: Geneerinen luokka mallin avulla template<typename T> class Lista {
/* listan toteutus... */
};
Lista<Elain> elainlista;
Lista<Auto> autolista;
C++: Geneerinen metodi mallin avulla template<typename T> void vaihda(T &a, T &b) { T apu = b; b = a; a = apu; } string terve = "taas!", taas = "terve "; vaihda(taas, terve); cout << terve << taas << endl;
Geneerisyys Javassa � Javassa käytössä ovat tyyppiparametrit
� korvataan oikealla tyypillä vasta ajon aikana � koskevat koko luokkaa tai yhtä metodia
� Tyyppiparametreja käyttävää luokkaa tai metodia sanotaan geneeriseksi
� Esimerkki: ArrayList<String> omaLista = new ArrayList<String>(); � sisältää yhden tyyppiparametrin � tyyppiparametri asetetaan String-tyyppiseksi luotaessa uusi ArrayList-instanssi
� Luokkaan voi liittyä useita tyyppiparametreja, jotka erotetaan pilkulla � esim. Map<Integer,String> kuvaus = new HashMap<Integer,String>
Geneerinen luokka: määrittely public class Pari<S,T> { private S eka; private T toka; public Pari(S e1, T e2) { eka = e1; toka = e2; } public S annaEka() { return eka; } public T annaToka() { return toka; } public void asetaEka(S e) { eka = e; } public void asetaToka(T e) { toka = e; } }
Geneerinen luokka: käyttö Pari<Integer, Integer> p1 =
new Pari<Integer, Integer>(1, 2);
Pari<String, Double> p2 =
new Pari<String, Double>("foo", 43.234);
Pari<Double, Pari<String, String>> p3 = new Pari<Double, Pari<String, String>>(19.1,
new Pari<String, String>("ab", "ba"));
Geneerinen luokka: käyttö Java 7:ssa “timantin” <> avulla
Pari<Integer, Integer> p1 = new Pari<>(1, 2);
Pari<String, Double> p2 = new Pari<>("foo", 43.234);
Pari<Double, Pari<String, String>> p3 = new Pari<>(19.1, new Pari<>("ab", "ba"));
Geneerinen luokka ja metodi � Luokka on geneerinen, mikäli se esittelee otsikossaan
tyyppiparametreja � tyyppiparametrit näkyvät luokan alueella � myös rajapinta voi esitellä tyyppiparametreja
� Metodi on geneerinen, mikäli se esittelee signatuurissaan tyyppiparametreja � tyyppiparametrit näkyvät metodin sisällä
� Esimerkki class Esimerkki { public <T> void show(T value) { System.out.println(value); } } nyt voidaan kutsua Esimerkki.<String>show("teksti"); Esimerkki.<Integer>show(42);
Tyyppiparametrin rajaus � Metodi tai luokka voi rajata
tyyppiparametrinsa � tyyppiparametrilta voidaan edellyttää tiettyä
roolia tai ominaisuutta � saa sisältää korkeintaan yhden konkreetin tai
abstraktin luokan � voi sisältää useita rajapintaluokkia (yhdistetään &-konnektiivilla)
� Rajaustyypit � extends: rajaus ylhäältä päin, minkä luokan
kanssa tyyppiparametrin on oltava yhteensopiva � super: rajaus alhaalta päin, minkä luokan
yliluokka tyyppiparametrin on oltava
Esimerkki: Tyyppiparametrin rajaus public class Tasopiste<N extends Number &
Comparable<? super N>> { private N x, y; public Tasopiste(N n1, N n2) { x = n1; y = n2; } public N annaX() { return x; } public N annaY() { return y; } public void asetaX(N n) { x = n; } public void asetaY(N n) { y = n; } } Tasopiste<Double> p1 = new Tasopiste<Double>(1.0,
1.3); Tasopiste<Integer> p2 = new Tasopiste<Integer>(3, -5);
Vapaa tyyppi 1(2)
� Tyyppiparametrit rikkovat ylityyppi–alityyppi -suhteen � oletetaan että Koira extends Eläin ja Kissa extends Eläin
� vaikka List on Collection-luokan alityyppi, ei List<Koira> ole Collection<Eläin>-luokan alityyppi
� korvausperiaatteen mukaan List<Koira>-tyypin pitäisi hyväksyä kaikki kutsut, jotka voi kohdistaa Collection<Eläin>-tyyppiin
Vapaa tyyppi 2(2) � Ratkaisu: vapaa tyyppi ?
� esim. Collection<?> on kaikentyyppisten kokoelmien, kuten List<Koira>-tyypin, ylityyppi
� voidaan rajoittaa kuten muitakin tyyppiparametreja: esim. Collection<? extends Eläin>
� Toimii myös metodeissa � String tulosta(List<?> lista) � String tulosta(List<? extends Henkilö>)
Tyyppitypistys ja raakatyyppi
� Parametrisoidut tyypit koodiksi 1. koodin erikoistaminen 2. koodin jakaminen
� Javassa käytössä tapa 2 � tyyppitypistys: kääntäjä poistaa
parametrisoidut tyypit ja rakentaa tarvittaessa siltametodeja
� tuloksena yksi .class-tiedosto � Raakatyyppi: List ≈ List<Object>
Geneerisyyden etuja
� Auttaa keskittymään tietorakenteen käyttäytymisen toteuttamiseen
� Helpottaa luokan yleistämistä � Johtaa yleiskäyttöisyyteen
� piirrevalikoima määritellään irti spesifistä kontekstista
� Tyyppiparametrien ominaisuuksien määrittely antaa käsityksen geneerisen luokan roolista luokkahierarkiassa
Yleistäminen � Toimii kahdella tasolla
� periytymisellä yleistetään mallinnettavia käsitteitä
� geneerisyydellä yleistetään käsiteltäviä tietoja
� Peukalosääntö: Yleistäminen kannattaa lopettaa viimeistään, kun uusien asiakkaiden kalastelu aiheuttaa enemmän työtä kuin nykyisten asiakkaiden palvelu
Periytyminen ja geneerisyys
List<Henkilö>
Collection<Henkilö>
ArrayList<Henkilö>
List<Tasopiste> List<Integer>
Geneerisyys (erikoistaminen)
Periytyminen
Yleistäminen
Erikoistaminen
Asiakas- ja periytymisrelaatioista
Asiakasrelaatio
� Asiakas käyttää vain julkisessa liittymässä kuvattuja piirteitä
� Olion yksityinen tieto (mm. toteutus) ei ole asiakkaan käytössä
� Toteuttaja helposti vaihdettavissa (kunhan se vain toteuttaa julkisen liitännän)
Periytymisrelaatio
� Vahvempi suhde sillä aliluokka näkee yliluokan sisäistä tietoa
� Aliluokka sidottu yliluokan julkiseen määrittelyyn (Liskovin korvausperiaate)
� Mahdollistaa monimutkaisen käsitehierarkian kuvaamisen
Käyttö- ja toteutussopimus
Asiakas
Toteuttaja
public
sisäinen toteutus
private
käyttösopimus
Uudelleen- käyttäjä
protected, public
toteutus- sopimus
Milloin periytyä, milloin olla asiakas? � Peukalosääntö 1: “is-a” vai “has-a”
� periytymien on olemista: “is-a” � asiakkuus on omistamista: “has-a”
� Peukalosääntö 2: julkisen liittymän laajuus � periytyessä luokka tarjoaa omien
piirteidensä lisäksi kaiken mitä yliluokka tarjoaa
� asiakkuudessa luokka voi määritellä itse oman julkisen liittymänsä
Is-a vai has-a?
� Asiakasrelaatio: “has-a” � Suutari “has-a” Naskali
� muuntuva ja helposti muokattava rakenne � “has-a” on harvoin “is-a”
� Periytymisrelaatio: “is-a” � Suutari “is-a” Työntekijä, Naskali “is-a” Työkalu
� jähmeä rakenne mutta voi vähentää koodia � “is-a” on usein “has-a”
Esimerkki: Is-a vai has-a?
Työntekijä
Suutari
Työkalu
Naskali
Luokkien välisten relaatioiden edut ja haitat
Ominaisuus Asiakasrelaatio Periytymisrelaatio
Informaation piilottaminen
Toteutuu Ei yleensä toteudu
Luokkien välinen riippumattomuus
Saavutetaan Ei saavuteta
Polymorfismi ja dynaaminen sidonta
Ei toimi Toimii
Luokan toteuttaminen Turhaa painolastia Yksinkertainen
Esimerkki: Ympyrä/ellipsi 1(2)
1. Ellipsi extends Ympyrä � ellipsi on erikoistunut ympyrä � tyyppiyhteensopivuus: ellipsi “is-a”
ympyrä? � entäpä piste: Ympyrä extends Piste?
2. Ympyrä extends Ellipsi � ympyrä on ellipsin erikoistapaus � ympyrän julkinen liitäntä sisältää ellipsin
käsitteitä? � entäpä piste?
Esimerkki: Ympyrä/ellipsi 2(2)
3. Ympyrä extends Kartioleikkaus, Ellipsi extends Kartioleikkaus � ympyrä ja ellipsi ovat kartioleikkauksia � 3-ulotteinen kappale yliluokkana? � entä avoimet kartioleikkaukset?
4. YmpyräEllipsi � kummatkin samassa luokassa � tehottomat toteutukset?
5. Ympyrä, Ellipsi � kummatkin omassa luokassaan � uudelleenkäytön puute?
Periaatteita
� Ilmauksien (funktiokutsujen) arvot riippuvat ainoastaan niiden argumenttien arvoista � ei sivuvaikutuksia (ts. ohjelmointia ilman
asetuslausetta) � Uusien ohjelmien (eli funktioiden)
rakentaminen � kompositio (eli yhdisteleminen) � rekursio
Funktionaalisten kielten ominaisuuksia � Funktiot ovat “ensimmäisen luokan
kansalaisia” � funktioilla on ohjelmoinnin kannalta sama arvo
kuin muillakin tietoalkioilla � Korkeamman asteen funktiot
� käsittelevät ja palauttavat muita (mahdollisesti suoritusaikana luotuja) funktioita
� Suoritusjärjestyksen vapaus � funktiot voidaan evaluoida missä järjestyksessä
tahansa (esim. laiskasti tai rinnakkaisesti) � Implisiittinen muistinhallinta
Funktionaalisia ohjelmointikieliä
� Lisp � ML � Erlang � Haskell � Scala � F# � Clojure
Lispin perusideat
� Lambda-kalkyyli � Täysin sulutettu puolalainen notaatio
� esim. (+ (* 1 2) 3) � “Lots of Infuriating, Stupid Parentheses”
� Linketyt listat ovat tärkein tietorakenne � LISP = LISt Processing � Lispin lähdekoodi itsessään on myös lista!
Lisp: hyvät ja huonot puolet � Syntaksin yksinkertaisuus
� helppo oppia (ohjelmat silti usein vaikealukuisia) � Tyypittömyys
� ohjelmat ovat luonnostaan polymorfisia � tyyppivirheet havaitaan yleensä vasta
suoritusaikana � Tulkitseva suoritus
� perinteisesti suoritettu tulkin alaisuudessa � Standardin puute
� kymmeniä erilaisia variaatioita eli murteita � käyttäjän määrittelemien ja systeemin valmiiden
funktioiden välillä ei ole mitään jyrkkää eroa
Lispin päämurteet Scheme � minimalismi: kielessä
mahdollisimman vähän toimintoja; muut kirjastoissa
� vaikutteet: lambda-kalkyyli, Algol ja Lisp (syntaksi)
� staattinen näkyvyysalue, heikosti tyypitetty
� sopii tranformationaaliseen ohjelmointiin
Common Lisp � kokosi yhteen
edeltävien Lisp-murteiden ominaisuudet
� tukee useita paradigmoja: imperatiivinen, funktionaalinen ja oliokeskeinen
� dynaaminen tyypitys
Esimerkki: Kuutio
(define (kuutio x)
(* x x x)
)
> (kuutio 3) 27
Esimerkki: Muunnos sekunneiksi
(define (muunna-sekunneiksi tunnit minuutit)
(* (+ (* tunnit 60) minuutit) 60)
)
> (muunna-sekunneiksi 7 45) 27900
Esimerkki: Kertoma (define (kertoma n) (cond ((zero? n) 1) (else (* n (kertoma (- n 1)))) ) )
> (kertoma 4) 24
Syntaksi
� Sulkumerkit � vaikuttavat ohjelman suoritukseen
� Prefix-notaatio � funktio vasemmalla, perässä argumentit � esim. (+ 1 2 3 4 5)
� Kommentit: puolipisteestä rivin loppuun � esim. (kuutio 3) ; kommentti
Ilmaukset
� Primitiiviset � muuttujat � literaalit � ehdot � funktiot � funktion kutsut
� Johdetut � muiden ilmausten avulla implementoidut
ilmaukset
Muuttujat
> (define x 42) > x 42
Literaalit
> (quote x) x > 'x x > 42 42 > "tere" "tere" > #t #t
Aritmeettiset funktiot
� +, -, *, / ja rem � Yhteen- ja kertolasku voidaan kohdistaa
kuinka moneen argumenttiin tahansa � esim. (+ 1 2 3) tai (* 4 5 6 7 8)
Testifunktiot � (equal? L M)
� ovatko L ja M rakenteeltaan samanlaiset � (eq? L M)
� viittaavatko L ja M samaan ilmaukseen � (atom? A), (pair? A), (null? A), (number? A) � ilmauksen tyypin (tai arvon) testaus.
� (= L M), (< L M), (> L M) � aritmeettiset vertailut
� Testifunktioiden tulos � atomi #t mikäli ominaisuus on voimassa � atomi #f mikäli ei ole
Totuusfunktiot � (not L)
� toteutuu mikäli L evaluoituu epätodeksi � (and L1 … Lk)
� toteutuu, mikäli kaikki argumentit evaluoituvat tosiksi
� oikosulkuevaluointi � (or L1 … Lk)
� toteutuu, mikäli yksikin argumentti evaluoituu todeksi
� oikosulkuevaluointi � totuusarvoja edustavat atomit #t ja #f
Ehdolliset ilmaukset: if
� (if ehto seuraus) � (if ehto seuraus vaihtoehto) � Esimerkki: (if (< x y)
(kuutio x)
(kuutio y))
Ehdolliset ilmaukset: cond
� (cond (ehto1 seuraus1) (ehto2 seuraus 2) … (else vaihtoehto))
� Esimerkki: (cond ((< x 0) 'negatiivinen) ((> x 0) 'positiivinen) (else 'nolla))
Lambda-kalkyyli
� Formalismi matemaattisten funktioiden käsittelyyn � yksinkertaisin mahdollinen tapa mallintaa
funktioiden määrittely ja soveltaminen � Formalismissa tarvitaan
� muuttujien joukko x, y, z… � sulut � symboli λ
Lambda-ilmausten määrittely
1. Muuttujat ovat λ-ilmauksia. 2. Jos M on λ-ilmaus ja x muuttuja, niin λx M
on λ-ilmaus. 3. Jos M ja N ovat λ-ilmauksia, niin (M N) on λ-ilmaus.
Sidottu ja vapaa muuttuja
� Muuttujan x esiintymä ilmauksessa M on sidottu, jos se sisältyy muotoa λx N olevaan M:n osailmaukseen
� Jos muuttujan esiintymä ei ole sidottu, se on vapaa
Ilmausten muuntaminen
� M[x/N] tarkoittaa että ilmauksessa M korvataan kaikki muuttujan x vapaat esiintymät ilmauksella N.
� Muunnossäännöt: � reduktio � korvaus
Reduktio
� Ilmaus (λx M N) redusoituu ilmaukseksi M[x/N]
� Reduktiosäännön soveltaminen vastaa parametrin N välittämistä funktiolle M � ts. x on muodollisen parametrin nimi
Korvaus
� Ilmaus λx M redusoituu ilmaukseksi λy M[x/y]
� Korvaussäännön avulla voidaan tarvittaessa uudelleennimetä funktion parametri, jotta reduktiosääntöä voidaan soveltaa (vrt. nimiparametrin välitys) � ei tarvita, jos muuttujanimet ovat
yksikäsitteisiä
Redusointi � λ-ilmaus on redusoitu, mikäli siihen ei voida
soveltaa reduktiosääntöä, vaikka kaikki muuttujat nimettäisiin uudelleen � redusoitu muoto vastaa ilmauksen esittämän
funktion arvoa � redusointi on funktion arvon laskemista eli
evaluointia � Churchin–Rosserin teoreema: Jokaisella
redusoituvalla λ-ilmauksella on yksikäsitteinen redusoitu muoto � ei takuuta että muutosjono olisi päättyvä…
Redusoinnin strategiat
� Sisältä-ulos (call by value) � reduktion kohteeksi vasemmanpuoleisin
alimman tason ilmaus � arvoparametri
� Ulkoa-sisään (call by name) � reduktion kohteeksi vasemmanpuoleisin
ylimmän tason ilmaus � laiska evaluointi
Esimerkki: Sisältä ulos
(λx (λy z x) P) → (λx z[y/x] P) = (λx z P) → z[x/P] = z
Esimerkki: Ulkoa sisään
(λx (λy z x) P) → (λy z x)[x/P] = (λy z P) → z[y/P] = z
Laiska evaluointi � Lisp evaluoi ilmauksia “innokkaasti” eli
ulkoa sisään � Joissain funktionaalisissa kielissä (esim.
Haskell) ilmauksia käsitellään laiskasti eli sisältä ulos � molemmat tavat johtavat kuitenkin samaan
lopputulokseen � Laiska evaluointi mahdollistaa äärettömät
tietorakenteet � tietorakenne on periaatteessa ääretön, mutta
sitä generoidaan (laiskasti) tarpeen mukaan
Lisp ja lambda-kalkyyli 1(2)
� Lisp on lambda-kalkyylin laajennus � ilmaukset voivat sisältää myös vakioita
(luvut, merkkijonot) � Tiettyihin funktiosymboleihin liittyy
erikoismerkitys ja erityinen reduktiosääntö � esim. (+ 1 2) tuottaa tulokseksi 3, vaikka
alkuperäinen ilmaus on jo redusoitu
Lisp ja lambda-kalkyyli 2(2)
� λ-ilmaus λx1 λx2 … λxn M kirjoitetaan muodossa (lambda (x1 x2 ... xn) M)
� λ-ilmaukselle voidaan antaa nimi, jota voidaan käyttää ilmauksen asemasta � esim. (lambda (x) (* x x x)) voidaan
nimetä kuutio
Esimerkki: Kuutio (define kuutio (lambda (x) (* x x x) ) ) ; eo. tarkoittaa samaa kuin (define (kuutio x) (* x x x) )
Esimerkki: Summaus (define summa (lambda (a b) (+ a b) ) ) (define monisumma (lambda (x . y) (apply + x y) ) )
Listat � Atomi nil on lista (tyhjä lista) � Jos S ja T ovat atomeja, listakonstruktori (S.T) yhdistää ne listaksi
� Jos L1, L2,…, Ln ovat atomeja tai listoja, niin (L1 L2 … Ln) on lista � merkintätapa tarkoittaa samaa kuin (L1.(L2.(…(Ln.nil)…)))
� Kaikki lambda-ilmaukset voidaan haluttaessa tulkita listoina � mahdollistaa ohjelmien kohtelemisen tietona ja
tietojen kohtelemisen ohjelmina
Listojen käsittely
� Listan pää: car � esim. (car '(a b c d e)) = a
� Listan häntä: cdr � esim. (cdr '(a b c d e)) = (b c d e)
� Lyhennysmerkintöjä � (cXYr L) = (cXr (cYr L)) � (cXYZr L) = (cXr (cYr (cZr L))) � esim. (car (cdr (cdr L))) voitaisiin
lyhentää (caddr L)
Esimerkki: listan alkioiden määrä
(define (alkioiden-lkm L)
(cond
((null? L) 0)
((null? (cdr L)) 1)
((null? (cddr L)) 2)
(else 'tosi-paljon)
)
Haskell: pääpiirteet
� Laiska evaluointi � Staattinen tyypitys � Tyyppipäättely (type inference)
Esimerkkejä: Aritmetiikkaa
2 + 3
4 * 4 - 3
42 / 2
(32 * 23) + (34 / 3)
4 * (-2)
Boolen algebra ja yhtäsuuruus
� Boolen operaatiot: True, False, not, &&, || � esim: not (True && True)
� Yhtäsuuruus == ja erisuuruus /=
Infix- ja prefix-funktiot
� Infix-funktioissa operaattori on operoitavien välissä � esim. 2 * 3
� Prefix-funktioissa operaattori operoitavien edessä � esim. succ 42, min 3 5
� Prefix-funktio voidaan kirjoittaa infix-muodossa lisäämällä takahipsut ` � esim. div 34 4, 34 `div` 4
Esimerkki: Funktioiden määrittely
tuplaaMinut x = x + x
tuplaaMeidat x y = x * 2 + y * 2
tuplaaMeidat' x y = tuplaaMinut x +
tuplaaMinut y
tuplaaPieniNumero x = if x > 100
then x
else x * 2
Listat 1(2) � Homogeenisia: kaikilla alkioilla sama tyyppi
� esim. numerot = [2, 4, 9, 11] � esim. nimi = "B. Virtanen"
� Listojen yhdistäminen ++ � esim. [1, 2, 3] ++ [4, 5, 6] � esim. "Lennart" ++ "Nilkén"
� Alkion liittäminen : � esim. 'M' : "urikka" � esim. 1 : [2, 3, 4]
Listat 2(2)
� Alkion haku indeksillä !! � esim. "Haskell" !! 3
� Leksikografinen vertailu <, <=, =>, > � Arvoväli ..
� esim. [1..10], ['a'..'z'] � myös askellus, esim. [2, 4..10]
Listakoosteet (list comprehensions) � Esim. [x * 2 | x <- [1..5]] � Predikaatit
� esim. [x * 2 | x <- [1..5], x * 2 > 5] � esim. [ x | x <- [10..20], x /= 10, x /= 15, x /= 19]
� esim. [ x * y | x <- [2,3], y <- [3,4,5]]
� Villikortti _ � esim. pituus xs = sum [1 | _ <- xs]
Tuplat � Voi sisältää eri tyyppejä � Tuplat on samanlaisia vain jos alkioiden
määrä ja tyypit vastaavat toisiaan � Esimerkkejä
� (1, 2) � [(1, 2), (3, 4), (5, 6)] � [("Fortran", 1954), ("Lisp", 1958), ("Algol", 1960)]
� Funktioita � fst(7, 3) palauttaa ensimmäisen alkion: 7 � snd(7, 3) palauttaa toisen alkion: 3 � zip [1..3] ['a'..'c'] yhdistää tuplat: [(1, 'a'), (2, 'b'), (3, 'c')]
Tyypit ja tyyppiluokat � Yleisimmät tyypit: Int, Integer, Float, Double, Bool, Char, Ordering
� Esimerkkejä � removeUpperCase :: [Char] -> [Char] � lisaaKolme :: Int -> Int -> Int -> Int
� Tyyppiluokka ≈ tyyppien rajapinta � mm. Eq, Ord, Show, Read, Bounded, Num, Integral
� esim. yhtasuuri :: (Eq a) => a -> a -> Bool
Tärkeimpiä tyyppiluokkia
Real Fractional Enum
Integral Floating
Bounded Ord Num
Eq
Read Eval Show
RealFrac
Funktiot: hahmon sovitus tarkoitus :: (Integral a) => a -> String tarkoitus 42 = "Oikein!" tarkoitus x = "Väärin!"
kertoma :: (Integral a) => a -> a kertoma 0 = 1 kertoma n = n * kertoma(n - 1)
Funktiot: vartijat bmiArvio :: (RealFloat a) => a -> a -> String
bmiArvio paino pituus
| bmi <= laiha = "Olet alipainoinen."
| bmi <= normaali = "Olet normaalipainoinen."
| bmi <= lihava = "Olet ylipainoinen."
| otherwise = "Olet vaarallisen ylipainoinen."
where bmi = paino / pituus ^ 2
(laiha, normaali, lihava) = (18.5, 25.0, 30.0)
Esimerkki: listan maksimialkio rekursiivisesti maksimi :: (Ord a) => [a] -> a
maksimi [] = error "tyhjä lista"
maksimi [x] = x
maksimi (x:xs)
| x > maxHanta = x
| otherwise = maxHanta
where maxHanta = maksimi xs
Esimerkki: listan täyttö tayta:: (Num i, Ord i) => i -> a -> [a]
tayta n x
| n <= 0 = []
| otherwhise = x:tayta (n-1) x
Esimerkki: alkioiden otto ota :: (Num i, Ord i) => i -> [a] -> [a]
ota n _
| n <= 0 = []
ota _ [] = []
ota n (x:xs) = x : ota (n - 1) xs
Esimerkki: listan kääntäminen
kaanna :: [a] -> [a]
kaanna [] = []
kaanna (x:xs) = kaanna xs ++ [x]
Korkeamman asteen funktiot � Ottavat funktioita parametrinaan ja
palauttavat funktioita paluuarvoina � Haskelin funktiot ovat unaarisia (ts. niillä
voi olla vain yksi parametri) � Curry-muunnoksella (currying) saadaan
aikaan vaikutelma että funktiolla on useampi parametri � esim. funktionkutsu max 7 3 luo funktion, jolla
on yksi parametri ja joka palauttaa 7 jos parametri on pienempi tai parametrin jos sen suurempi kuin 7
Osittain sovelletut funktiot
� Jos funktion kutsussa jätetään pois parametreja, saadaan osittain sovellettu funktio � voidaan luoda lennossa uusia funktioita
Esimerkki: osittain sovellettu funktio kerroKolme :: (Num a) => a -> a -> a -> a
kerroKolme x y z = x * y * z
kerroKaksiKuudella :: (Num a) => a -> a -> a
kerroKaksiKuudella = kerroKolme 6
kerroKahdellatoista :: (Num a) => a -> a
kerroKahdellatoista = kerroKaksiKuudella 2
Esimerkki: funktio parametrina
sovellaKahdesti :: (a -> a) -> a -> a
sovellaKahdesti f x = f (f x)
Map
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
Filter
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs
Esimerkki: pikalajittelu pikalajittele :: (Ord a) => [a] -> [a]
pikalajittele [] = []
pikalajittele (x:xs) =
pikalajittele pienet ++ [x] ++
pikalajittele suuret
where pienet = filter (< x) xs
suuret = filter (>= x) xs
Muita Haskelin piirteitä
� Funktorit � Monadit
Syöttö- ja tulostus main = do
putStrLn "Hei, mikä on nimesi?"
nimi <- getLine
putStrLn ("Hei " ++ nimi ++ ", hyvin menee!")
IO String String
Mitä on logiikkaohjelmointi? � Logiikkaohjelma koostuu joukosta logiikan
kaavoja � logiikkaohjelmointi on näiden kaavojen
kirjoittamista � Ohjelman suoritus muodostuu ohjelman
loogisten seurausten automaatisesta päättelystä
� Ohjelmien merkitys (ideaalisti) on tajuttavissa ilman mitään tietoa niiden suoritustavasta � kaavat ovat joko tosia tai epätosia riippumatta
siitä, miten itse todistus suoritetaan
Mitä ohjelmointi on?
Ohjelma = algoritmit + tietorakenteet
– Niklaus Wirth
Algoritmi = logiikka + kontrolli
– Robert Kowalski
Propositiologiikkaa � Propositio
� väittämä joka voi olla tosi tai epätosi � suljettu lause
� Symbolinen logiikka � propositioiden esittäminen � propositioiden välisten suhteiden (eli
relaatioiden) esittäminen � uusien propositioiden päättely (eli
johtaminen) aikaisemmista tosista propositioista
Propositiologiikan syntaksia
� Propositiosymbolit � Loogiset konnektiivit: ⋀, ⋁, ¬, →, ↔ � Päättelyoperaattorit: ⟹, ⟺ � Propositiologiikan lakeja
� esim. vaihdantalait, liitäntälait, de Morganin lait
Predikaattilogiikka � Formalismi selittävien eli deklaratiivisten
lauseiden matemaattiseen esittämiseen � luonnollisen kielen ilmaus (eli väittämä) joka voi
olla joko tosi tai epätosi � kohdistuvat jonkin tarkastelujoukon (eli
universumin) alkioihin ja niiden välisiin suhteisiin (eli relaatioihin)
� Siirros propositiologiikasta: � propositioihin liitetään parametrit (eli predikaatti
voi olla avoin lause) � kvanttorit: ∀, ∃
Avoimet ja suljetut lauseet
� Muuttuja on sidottu jos se on kvanttorin vaikutuspiirissä; muutoin se on vapaa � esim. ∃X: X + Y = 1; X on sidottu, Y on
vapaa � Lause on suljettu, jos se ei sisällä
vapaita muuttujia; muutoin se on avoin � suljetulla lauseella on totuusarvo
� Avoimen lauseen totuusarvo riippuu vapaille muuttujille annettavista arvoista
Looginen päättely
� Johtopäätösten muodostamista muista lauseista formaalien päättelysääntöjen avulla
� Päättelysääntöjä � modus ponens (esim. X ⋀ (X → Y) ⟹ Y) � kvanttorin eliminointi
(esim. oletetaan X ∈{ X1, X2, … Xn }, ∀X: P(X) ↔ P(X1) ⋀ P(X2) ⋀ … ⋀ P(Xn) ∃X: P(X) ↔ P(X1) ⋁ P(X2) ⋁ … ⋁ P(Xn)
Automaattinen teoreemantodistus � Predikaattilogiikan teoreemantodistus
liian vaikeaa � on olemassa ongelmia joihin ei ole
algoritmista ratkaisua � Automaattinen teoreemantodistus
onnistuu vain syntaksiltaan rajoitetuille kaavoille � definiitti klausuuli � voidaan soveltaa yksinkertaista ja tehokasta
SLD-päättelysääntöä
Definiitti klausuuli
� Ilmaistaan kahdentyyppistä tietoa: � fakta: jokin relaatio on (varauksetta)
voimassa tiettyjen objektien välillä � sääntö: relaatio on voimassa kunhan jotkin
muut relaatiot ovat � Klausuulin syntaktinen muoto (eli Hornin
klausuuli) H ← B1 ⋀ … ⋀ Bn (n ≥ 0) � jos n = 0, kyseessä on fakta, muutoin sääntö
Definiitti logiikkaohjelma
� Äärellinen joukko definiittejä klausuuleita � Tehtävien johtopäätösten joukko on
yleensä ääretön � Esittämällä koneelle yksittäinen kysely
(eli maali) selvitetään onko se ohjelman seuraus vai ei � kyselyn syntaktinen muoto: ← B1 ⋀ … ⋀ Bm
Prolog � Sopii ohjelmiin joissa käytetään symbolista
tai ei-numeerista laskentaa � esim. tekoälysovellukset
� Koostuu joukosta sääntöjä ja faktoja � ohjelma ajetaan esittämällä kysely ja toteamalla
voidaanko se perustella niiden perusteella � Lauseet muodostetaan seuraavista
termeistä � vakio (kokonaisluku tai atomi) � muuttuja � funktori
Prolog-lauseet � Faktat
� lauseita joiden oletetaan olevan tosia � käyttäjän määräämä merkitys
� Säännöt � vastaavat matematiikan teoreemoja, joista
voidaan vetää johtopäätöksiä jos tietyt ehdot toteutuvat
� Kyselyt � tavoitteena selvittää onko annettu kysely tosi vai
ei (eli “Yes” tai “No”) � järjestelmä antaa ne muuttujien instanssit joilla
lause on tosi
Aritmetiikkaa
� Samastaminen: = � esim. X = 3 + 4, muuttuja X samastetaan
termiin 3 + 4 � Lausekkeen evaluointi: is
� esim. X is 3 + 4, muuttujaan X sijoitetaan termi 7
Yksinkertaiset faktat
� Faktat muodostuvat tietystä asiasta tai asioiden välisestä relaatiosta
� Esimerkki: aurinkoista. minkä jälkeen tehdään Prologille kysely ?- aurinkoista. Yes
� Syntaksi: alkaa pienellä kirjaimella ja päättyy pisteeseen
Relaatiofaktat
� Faktaan voi liittyä yksi tai useampia argumentteja
� Esimerkki: tykkaa(jussi, maria).
� huom. relaatiolla ei ole suuntaa (siis Jussi voi tykätä Mariasta tai päinvastoin)
Esimerkki: relaatiofaktoja 1 syo(risto, omena). syo(risto, kyljys). syo(tommi, banaani). syo(jussi, banaani). syo(jussi, kiwi). ?- syo(risto, omena). Yes ?- syo(jussi, banaani). Yes ?- syo(mika, banaani). No ?- syo(risto, banaani). No
Esimerkki: relaatiofaktoja 2 ika(jussi, 23). ika(amalia, 14). ika(ylermi, 47). ika(iikka, 5). ika(tuomas, 35). ?- ika(iikka, 5). Yes ?- amalia(14). No ?- ika(iikka, viisi). No
Muuttujat ja samastaminen
� Muuttujat alkavat isolla kirjaimella � Esim. oletetaan että meillä on fakta syo(risto, omena). kuinka kysytään mitä Risto syö? ?- syo(risto, Mita). Mita=omena Yes
� muuttuja Mita samastuu arvoon omena
Esimerkki: muuttujat 1 tykkaa(jussi, maria). tykkaa(risto, askartelu). ?- tykkaa(jussi, Kuka). Kuka=maria Yes ?- tykkaa(arska, Kuka). No ?- tykkaa(risto, Kuka). Kuka=askartelu Yes
Esimerkki: muuttujat 2 levy(1, pink_floyd, meddle, echoes).
levy(2, led_zeppelin, physical_graffiti, trampled_under_foot).
levy(3, the_beatles, abbey_road, something).
levy(4, genesis, foxtrot, suppers_ready).
levy(5, rolling_stones, sticky_fingers, brown_sugar).
?- levy(3, Artisti, Albumi, Lemppari).
Artisti=the_beatles
Albumi=abbey_road
Lemppari=something
Yes
?- levy(2, led_zeppelin, physical_graffiti, Lemppari).
Lemppari=trampled_under_foot
Yes
Säännöt � Mahdollistavat ehdolliset lauseet
universumista � jokaisella säännöllä on monta variaatiota joita
sanotaan klausuuleiksi � klausuulit antavat erilaisia mahdollisuuksia
päättelyyn � Esim. “Kaikki ihmiset ovat kuolevaisia” kuolevainen(X) :- ihminen(X). � deklaratiivinen tulkinta: Annetulle X:lle, X on
kuolevainen, jos X on ihminen � proseduraalinen tulkinta: Todistaaksesi maali X
on kuolevainen, todista alimaali X on ihminen
Esimerkki: säännöt 1
kuolevainen(X) :- ihminen(X).
ihminen(sokrates).
?- kuolevainen(sokrates).
Yes
?- kuolevainen(P).
P=sokrates
Yes
Esimerkki: säännöt 2 hauskaa(X) :- punainen(X), auto(X). hauskaa(X) :- sininen(X), mopo(X). auto(lada). auto(mosse). mopo(pappa_tunturi). punainen(lada). punainen(mosse). sininen(pappa_tunturi). ?- hauskaa(pappa_tunturi). Yes ?- hauskaa(Mika). Mika=lada; Mika=mosse; Mika=pappa_tunturi; No
Esimerkki: peräytys (backtracking) pitaa_juhlat(X) :- syntymapaiva(X), onnellinen(X).
syntymapaiva(tommi).
syntymapaiva(risto).
syntymapaiva(henna).
onnellinen(maria).
onnellinen(jaana).
onnellinen(henna).
?- pitaa_juhlat(Kuka).
Kuka=henna
Yes
Esimerkki: rekursio 1 matkalla(rooma).
matkalla(Paikka) :-
siirry(Paikka, Kulkuneuvo, UusiPaikka),
matkalla(UusiPaikka).
siirry(koti, taksi, tku).
siirry(tku, lentokone, hel).
siirry(hel, lentokone, rooma).
?- matkalla(koti).
Yes
Esimerkki: rekursio vanhempi(jussi, paavo).
vanhempi(paavo, tuomas).
vanhempi(tuomas, maria).
esivanhempi(X, Y) :- vanhempi(X, Y).
esivanhempi(X, Y) :- vanhempi(X, Z),
esivanhempi(Z, Y).
?- esivanhempi(jussi, tuomas)
Yes
Listat
� Syntaksi: luetellaan alkiot hakasuluissa pilkuilla erotettuina � esim. [a, risto, Muuttuja, omena] � tyhjä lista: []
� Pään ja hännän erottaminen: | � esim. [yksi, kaksi, kolme] = [A|B],
missä A = yksi ja B = [kaksi, kolme]
Esimerkki: listat p([Paa|Hanta], Paa, Hanta). ?- p([a, b, c], X, Y). X=a Y=[b, c] Yes ?- p([a], X, Y). X=a Y= [] Yes ?- p([], X, Y). No
Esimerkki: listasta haku on(Alkio, [Alkio|Loput]).
on(Alkio, [JataPaaValiin|Hanta]) :- on(Alkio, Hanta).
?- on(omena, [kiwi, kurkku, omena, banaani]).
Yes
Esimerkki: listan rakentaminen
yhdista([], Lista, Lista).
yhdista([Paa|Hanta], Lista2, [Paa|Tulos]) :-
yhdista(Hanta, Lista2, Tulos).
?- yhdista([a, b, c], [yy, kaa, koo], Tulos).
Tulos = [a, b, c, yy, kaa, koo]
Yes
Esimerkki: listan suodatus suodata([], []).
suodata([X|Hanta], [X|Tulos]) :-
X > 6, suodata(Hanta, Tulos).
suodata([HeitaPois|Hanta], Tulos) :-
suodata(Hanta, Tulos).
?- suodata([1, 12, 3, 14, 5, 8], Tulos).
Tulos=[12, 14, 8]
Yes
sisaan!
kaivo!
limbo!
keiju!
rosvot!
ulos!
ruokaa!
aarre!
peikot!
Esimerkki: Labyrintti 1(3) naapurit(sisaan, peikot). naapurit(sisaan, kaivo). naapurit(kaivo, limbo). naapurit(kaivo, ruokaa). naapurit(kaivo, rosvot). naapurit(kaivo, keiju). naapurit(rosvot, aarre). naapurit(rosvot, ulos). naapurit(ruokaa, aarre). naapurit(peikot, aarre). naapurit(keiju, ulos). naapurit(aarre, ulos). valta([peikot, rosvot]). kulje(Taalta, Tuonne) :- valta(Vaarat), polku(Taalta, Tuonne, Vaarat, [Taalta]).
Esimerkki: Labyrintti 2(3) polku(Tama, Tama, Vaarat, Vana) :- jasen(aarre, Vana), kaanteiskirjoita(Vana). polku(Mista, Mihin, Vaarat, Vana) :- (naapuri(Mista, Etappi); naapuri(Etappi, Mihin)), not jasen(Etappi, Vaarat), not jasen(Etappi, Vana), polku(Etappi, Mihin, Vaarat, [Etappi|Vana]). kaanteiskirjoita([]). kaanteiskirjoita([Paa|Hanta]) :- kaanteiskirjoita(Hanta), nl, write(Head). jasen(X, [X|_]). jasen(X, [_|Y]) :- jasen(X, Y).
Esimerkki: Labyrintti 3(3)
?- kulje(sisaan, ulos).
sisaan
kaivo
ruokaa
aarre
ulos
Yes
Prologin puutteita � Resoluutiojärjestyksen kontrollointi
� järjestys vaikuttaa tehokkuuteen � ikuiset silmukat mahdollisia
� Oletus suljetusta maailmasta � tavoite voidaan osoittaa todeksi muttei
epätodeksi � Negaatio-ongelma � Luontaiset rajoitukset
� ei tarvitse kuvata, miten jokin tehtävä suoritetaan
Käsiteltävät aiheet
� Skriptiohjelmointi � Prototyyppipohjaiset kielet � Kompositiopohjainen ohjelmointi
(concatenative programming) � Tietovuo-ohjelmointi (dataflow
programming) � Visuaalinen ohjelmointi � Esoteeriset ohjelmointikielet
Skriptiohjelmointi
� Olemassa olevien ohjelmistokomponenttien yhdistely
� Joustavuus: tyypittömyys tai dynaaminen tyypitys
� Tulkattavuus � Upottaminen muihin tiedostoihin (esim.
HTML-dokumentteihin) � Refleksiivisyys
Historiaa � Kaksi esivanhempaa
� komentotulkit (eli kuoret, shells) ○ JCL, MS-DOSin command, Unixin sh ja csh
� tekstinkäsittelyn ja raportingeneroinnin työkalut ○ IBM:n RPG, Unixin sed ja awk
� Yleiskielet � IBM:n Rexx � Perl � muita: Tcl, Python, Ruby, VBScript, AppleScript
� Skriptikielet yleistyivät web-sovellusten myötä � PHP � JSP, Ruby on Rails, VBScript � JavaScript
Vertailua � Perinteinen ohjelmointikieli
� tarkoitettu itsenäisten sovellusten rakentamiseen ○ syöte, tiedon käsittely, tulostus
� Skriptikieli � tarkoitettu useiden sovellusten koordinointiin ○ testit ja ehdot, silmukat, muuttujat ja tyypit,
aliohjelmat ja abstraktiot � toimii sovellusten ulkopuolella (“liimaa” ne
yhteen)
“Scripting languages assume that a collection of useful components already exist in other languages. They are intended not for writing applications from scratch but rather for combining their components.”
– John Ousterhout
Yhteisiä piirteitä 1(3) � Sekä eräajo- että vuorovaikutteinen
käyttö � harvoilla kielillä on kääntäjä (poikkeus Perl)
� Ilmausten ekonomisuus � kaiken ylimääräisen minimointi: välimerkit,
lyhyet avainsanat � Määrittelyjen puuttuminen;
yksinkertaiset näkyvyyssäännöt � oletuksena kaikki globaalia (esim. Perl) tai
lokaalia (esim. PHP ja Tcl)
Yhteisiä piirteitä 2(3) � Joustava dynaaminen tyypitys
� tyyppi tarkistetaan joko ennen käyttöä (esim. PHP, Python) tai muuttuja tulkitaan eri tavalla eri kontekstissa (esim. Rexx, Perl, Tcl) ○ esim. Perl-ohjelma
$a = "4"; print $a . 3 . "\n"; print $a + 3 . "\n";
tulostaa 43 ja 7
� Helppo pääsy alla olevan järjestelmän palveluihin
Yhteisiä piirteitä 3(3)
� Kehittyneet operaatiot hahmonsovitukseen ja merkkijonojen käsittelyyn � pohjautuvat usein säännöllisiin ilmauksiin
(regular expression) � Korkean tason tietotyypit
� esim. joukot, listat, tuplat, kuvaukset � roskien keruu
Prototyyppipohjainen ohjelmointi
� Tukee monia olio-ohjelmoinnin piirteitä � kapselointi � jäsenmuuttujat ja metodit
� Ei luokkamäärityksiä � oliot luodaan joko tyhjästä tai kloonaamalla
olemassa oleva olio � Periytyminen: kloonataan toinen olio
(joka on siis uuden olion prototyyppi) � Kieliä: JavaScript, Lua, Self, Io
Miksei periytymistä vaan prototyyppejä? � Oliokielissä ohjelmoija keskittyy usein ensin
luokkien taksonomiaan ja niiden välisiin suhteisiin
� Prototyyppipohjaisisssa kielissä � ohjelmoija keskittyy ensin muutamasta oliosta
muodostuvan joukon käyttäytymiseen � myöhemmin olioita luokitellaan arkkityyppisiksi
olioiksi � prototyyppejä ohjataan muokattavaksi ajoaikana (vrt.
oliokielten luokkien käännösaikainen määrittely) � Lähes kaikki prototyyppipohjaiset kielet ovat
tulkittavia ja dynaamisesti tyypitettyjä
Olion luonti � Luokkapohjaisissa kielisssä olio luodaan
konstruktorin avulla � instanssi saa kaikki luokassa määritellyt piirteet
� Prototyyppipohjaisissa kielissä ei ole luokkia vaan olion periytyvät suoraan toisista olioista (joka on siis uuden olion prototyyppi)
� Konstruointi � olio luodaan tyhjästä (ex nihilo) � olio luodaan kloonaamalla olemassa oleva olio
� Konstruoinnin jälkeen oliota voidaan muuntaa halutulla tavalla
Kritiikkiä � Oikeellisuus
� luokat ovat analogisia tyyppien kanssa, joten ne tarjoavat olioille ja niiden käyttäjille takeet siitä että oliot käyttäytyvät tietyllä tavalla
� Turvallisuus � Ennustettavuus � Tehokkuus
� luokkia käytettäessä kääntäjä voi tehdä enemmän optimointeja kuin prototyyppien kanssa
Kompositiopohjainen (concatenative) ohjelmointi � Käyttää funktion kompositiota
(composition) funktion soveltamisen (application) sijaan
� Funktio � saa mielivaltaisen määrän syötteitä � käyttää niistä vain päällimmäisiä � palauttaa käyttämättömät syötteet, joita seuraa
varsinainen tulos � Siis kyseessähän on… pino! � Ohjelmat ovat yksinkertaisia ja triviaaleja
suorittaa tehokkaasti
Esimerkki: 2 3 × 4 5 × + Funktio Tulos
() 2 (2) 3 (2, 3) × (6) 4 (6, 4) 5 (6, 4, 5) × (6, 20) + (26)
Kompositiopohjaisia kieliä � Java Virtual Machine
� tavukoodi
� CPython-tavukoodi � käyttäjiä mm. BitTorrent, Dropbox ja YouTube
� PostScript-kuvauskieli � Forth-kieli
� sulautetut järjestelmät � Open Firmware, OpenBoot � avaruussovellukset
Tietovuo-ohjelmointi (dataflow programming) � Mallintaa ohjelman suunnattuna graafina,
joka kuvaa kuinka tieto virtaa operaatioiden välillä
� Pyrkii määrittämään, kuinka asiat liittyvät toisiinsa � vrt. imperatiivinen ohjelmointi määrittelee kuinka
asiat tapahtuvat (ts. tietovuo on toissijasta kontrollivuon rinnalla)
� Tietovuo-ohjelma on sarja (mahdollisesti toisiinsa liittyviä) yhteyksiä � operaatiot ovat toissijaisia
� Historiaa: Unixin putket
Tilan käsite � Normaalisti tietokone ei tiedä että jokin
informaation pala sisältää tilan � sen sijaan: informaation pala on väliaikaista ja
voidaan pian hylätä � Ongelma: tilatietoa voidaan joutua
jakamaan usean prosessorin kesken esim. rinnakkaislaskennassa � ohjelmoijan täytyy laittaa lisäkoodia kertomaan
mikä tieto on tärkeää ○ lisää suoritusaikaa, vaikeaa debugata, rumaa
� Tietovuo-ohjelmoinnissa tilan käsite ei häviä
Tietovuo-ohjelma
� Ohjelmat alkavat syötteestä, joka kuvaa kuinka tietoa käytetään ja muunnetaan � tieto on nyt eksplisiittistä (voidaan kuvata
jopa graafisesti) � Operaatiot ovat “mustia laatikkoja”
� vrt. sarja työläisiä asennuslinjalla � rinnakkaistuvaa
Visuaalinen ohjelmointi
� Ohjelmia luodaan käsittelemällä elementtejä graafisesti � ikoni-pohjaiset � lomakepohjaiset � diagrammipohjaiset
� Nykypyrkimyksenä visuaalisen ja tietovuo-ohjelmoinnin yhdistäminen
Esimerkki: Scratch
Esoteeriset ohjelmointikielet � Tarkoituksena on poistaa tai korvata
konventionaalisia kielten piirteitä säilyttäen Turing-täydellisyys � ohjelmointikielten rajojen tutkiminen � ohjelmointikielten parodisointi
� Esimerkkejä � INTERCAL � FALSE � brainfuck � Befunge � Piet � Shakespeare � Whitespace
INTERCAL: Hello, world DO ,1 <- #13 PLEASE DO ,1 SUB #1 <- #238 DO ,1 SUB #2 <- #108 DO ,1 SUB #3 <- #112 DO ,1 SUB #4 <- #0 DO ,1 SUB #5 <- #64 DO ,1 SUB #6 <- #194 DO ,1 SUB #7 <- #48 PLEASE DO ,1 SUB #8 <- #22 DO ,1 SUB #9 <- #248 DO ,1 SUB #10 <- #168 DO ,1 SUB #11 <- #24 DO ,1 SUB #12 <- #16 DO ,1 SUB #13 <- #162 PLEASE READ OUT ,1 PLEASE GIVE UP
Brainfuck-kielen 8 komentoa Komento Toiminto
> Lisää osoittimen osoitteeseen yksi < Vähennä osoittimen osoitteesta yksi + Lisää osoittimen osoittamaa tavua yhdellä - Vähennä osoittimen osoittamaan tavua yhdellä . Tulosta osoittimen osoittama tavu ASCII-merkkinä , Lue syötteestä arvo osoittimen osoittamaan tavuun [ Jos osoittimen osoittama tavu on nolla, hyppää
eteenpäin vastaavaan ]-merkkiin ] Jos osoittimen osoittama tavu ei ole nolla, hyppää
taaksepäin vastaavaan [-merkkiin
Esimerkki: Hello, World!
++++++++++[>+++++++>+
+++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>
++.<<+++++++++++++++.
>.+++.------.--------
.>+.>.
Esimerkki: Hello, world! (kommentoituna) +++++ +++++ initialize counter (cell #0) to 10
[ use loop to set the next four cells to 70/100/30/10
> +++++ ++ add 7 to cell #1
> +++++ +++++ add 10 to cell #2
> +++ add 3 to cell #3
> + add 1 to cell #4
<<<< - decrement counter (cell #0)
]
> ++ . print 'H'
> + . print 'e'
+++++ ++ . print 'l'
. print 'l'
+++ . print 'o'
> ++ . print ' '
<< +++++ +++++ +++++ . print 'W'
> . print 'o'
+++ . print 'r'
----- - . print 'l'
----- --- . print 'd'
> + . print '!'
> . print '\n'
Befunge: Hello, World!
> v
v ,,,,"Hello,"<
>48*, v
v,,,,,,"World!"<
>25*,@
Piet: Hello World
Kurssin arvostelu � Arvostelu
perustuu 100 pisteeseen
� Pistejako � tentti: 60 pistettä � demonstraatiot:
20 pistettä � harjoitustyö: 20
pistettä
� Hyväksytty kurssi vaatii yhteensä 45 pistettä
� Arvosanajako � [45,55) ⇒ 1 � [55, 65) ⇒ 2 � [65, 75) ⇒ 3 � [75, 85) ⇒ 4 � [85, 100] ⇒ 5
Harjoitustyö � Tehdään 4. periodissa
� valitse annetuista vaihtoehdoista yksi ohjelmointikieli ja yksi tehtävä
� laadi ratkaisustasi 5–10 sivuinen raportti � palauta moodlen kautta 5.5.2014 klo 14:00
mennessä � Arvostelu
� hylätty (0 pistettä) � välttävä (5 pistettä) � tyydyttävä (10 pistettä) � hyvä (15 pistettä) � kiitettävä (20 pistettä)
� Ohjeet moodle-sivulla (ks. bit.ly/okp2014)
Tentit
� Sähköinen tentti � avautuu 5.5.2014 � sulkeutuu 30.9.2014
� Tentin voi suorittaa enintään kolme (3) kertaa
� Lisäohjeita ja ajanvaraus: https://tenttis.utu.fi
Miksi tutustua erilaisiin ohjelmointikieliin? � Ajatusten esitttämiskyky laajenee � Riittävät taustatiedot sopivan
ohjelmointikielen valintaan � Kyky omaksua uusia ohjelmointikieliä � Kielen toteutuksen merkityksen
ymmärtäminen � Kyky seurata, ymmärtää ja arvioida
ohjelmointikielten kehitystä
Kaksi kirjasuositusta
Bruce A. Tate: Seven Languages in Seven Weeks. Pragmatic Bookshelf, 2010.
Federico Biancuzzi & Shane Warden (toim.): Masterminds of Programming. O’Reilly Media, 2009.