Ohjelmointikielet ja -paradigmat - kalvot 2014

Preview:

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.

Recommended