Tyypit (eri näkökulmia) Tyypit arvojen joukkona tyyppi kerää yhteen erilaiset primitiiviarvot (kokonaisluvut, liukuluvut) oleellista arvojen esitystapa tietokoneessa Tyyppien rakentaminen skalaarityypit (primitiiviset tyypit, atomiset tyypit) tyyppikonstruktorit (rakenteisten tyyppien mahdollistaminen) arvojoukko ja operaatiot nähdään tyypin luonnollisina ominaisuuksina Tyypit abstrahointivälineenä kielen rakenteiden avulla voidaan muodostaa tyyppejä ja määritellä niille operaatioita tyyppi nähdän rajapintana (moduulit, luokat) 1
Tyyppeihin liittyviä kysymyksiä Onko tyyppi staattinen vai dynaaminen käsite? ovatko tyypin ominaisuudet tiedossa jo käännösaikana vai vasta ajoaikana Sidotaanko muuttujien tyypit staattisesti vai dynaamisesti? Voiko tyyppejä parametroida? ovatko parametrit staattisia vai dynaamisia mitkä osat tyyppimäärittelyssä ovat parametroitavissa Miten määritellään tyyppien ekvivalenssi? milloin kaksi muuttujaa ovat samaa tai yhteensopivaa tyyppiä Esimerkkikoodit kuvitteellisella kielellä var X: Integer;... type T = array [ 1..X ] of Integer; type T ( Max: Integer ) = 1..Max;... var A: T ( 10 ); procedure P ( X: Integer ); var A: T ( X );... 2
Vahva tyypitys Muita määritelmiä: tyyppivirheet huomataan (joko käännös- tai ajoaikana) tyypille sopimattomat operaatiot estetään Kieli on vahvasti tyypitetty, jos jokaisella tietoalkiolla (vakio, muuttuja, kenttä) on yksikäsitteinen tyyppi, jonka identiteetti ja ominaisuudet ovat tiedossa käännösaikana tyyppimuunnokset tapahtuvat kontrolloidusti tulkitsemalla arvo toisen tyypin arvona eivät tulkitsemalla arvon esitys toisen tyypin arvon esityksenä tyyppimuunnosten laillisuus on selvitettävissä käännösaikana Staattinen tyypitys jokaisen muuttujan tyyppi on tiedossa käännösaikana Vahva tyypitys käännösaikainen tyypitys 3
Tyypitys ohjelmointikielissä Fortran (heikko tyypitys) parametrivälityksessä ei tyyppitarkistusta Equivalence-lause Pascal (lähes vahva tyypitys) vaihtelevat tietueet eivät välttämättä turvallisia Ada (vahva tyypitys) vaihtelevat tietueet turvallisia C ja C++ (heikko tyypitys) parametrivälityksessä tyyppejä ei välttämättä tarkisteta union-rakenne ei turvallinen implisiittiset tyyppimuunnokset Java ja C# (vahva tyypitys) 4
Vahva tyypitys Tyyppimuunnokset cast eksplisiittinen muunnos coersion implisiittinen muunnos Vahva tyypitys (Ada) eksplisiittiset muunnokset sallittuja vain rajoitetusti poikkeuksena: Unchecked_Conversion Salliva kieli (C) kääntäjä voi tehdä automaattisesti tyyppimuunnoksia pakotetut (implisiittiset) tyyppimuunnokset johdonmukaisuuden puute milloin ja millaisia automaattisia muunnoksia tapahtuu milloin eksplisiittinen muunnos on mahdollinen 5
Ohjelmointikielissä esiintyviä tyyppejä Skalaarityypit Luetellut tyypit Osävälityypit, alityypit, johdetut tyypit Rakenteiset tyypit muodostetaan tyyppikonstruktoreiden avulla Osoitintyypit Joukkotyyppi (set) Aliohjelma- ja funktiotyypit tarkastellaan aliohjelmien yhteydessä Tehtävätyypit (Ada) liittyvät rinnakkaisuuteen, käsitellään siinä yhteydessä 6
Numeeriset tyypit Numeerisen tiedon esittäminen tehokkuussyistä kannattaa käyttää tietokoneen sisäistä esitystapaa siirrettävyyttä vaikeuttaa lukujen määrittelyjoukon vaihtelut Kokonaislukujen esitysmuoto: kahden komplementti 2 n-1..2 n-1-1 esim. 16 bittiä: -32768..32767 Liukulukujen esitysmuoto: esitys noudattaa standardia Kokonaislukujen käyttö: matemaattisena arvona silmukan laskurina taulukon indeksinä s eksponentti 1 8 23 mantissan kasvattaminen suurentaisi tarkkuutta eksponentin kasvattaminen suurentaisi arvoaluetta Desimaaliluvut (Cobol, C#) kiinteä määrä numeroita, desimaalipilkku kiinteässä kohdassa suuri tarkkuus, pieni arvoalue mantissa 7
Merkkityypit Yhden merkin esittäminen ohjelmointikielessä Tyypillisiä operaatioita: Yhtäsuuruusvertailut, aakkosjärjestysvertailu (?) I/O Muunnokset merkkijonotyypiin ja takaisin Muunnokset isoista kirjaimista pieniin yms. Sisäinen toteutus tietokoneen muistissa Vanhat koodaukset (EBCDIC IBM-koneissa) ASCII (ISO-646) (7 bittiä) ISO-latin-koodaukset yms. 8-bittiset koodaukset Unicode (32 bittiä) (ennen myös 16-bittinen versio) 8
Merkkityyppien ongelmia Merkkityypin koko ja numeroarvon tulkinta Perinteisesti 8 bittiä (aluksi 6-7), sallii maks. 256 eri merkkiä Ei riitä alkuunkaan ei-englanninkielisessä maailmassa! Ratkaisu 1: useita merkistökoodauksia, samalla numerokoodilla eri tulkinta (iso-latin-x) Ratkaisu 2: suurempia merkkityyppejä (Unicode, 16- ja 32-bittiset) Missä määrätään käytetty koodaus? Lähdekooditiedoston koodaus (merkki- ja merkkijonoliteraalit, tunnisteet) Talletus muistissa (ohjelman sisäinen merkki/merkkjonotyypin koodaus) Muunnokset I/O:n yhteydessä Muunnos käyttäjän koodauksen ja sisäisen koodauksen välillä syötteenluvun ja tulostuksen yhteydessä Muunnos tiedostojen koodauksen ja sisäisen koodauksen välillä 9
Vertailu Merkkityyppien ongelmia Miten määrätään aakkosjärjestys? Riippuu kielestä (ja maasta)! Onko aakkosvertailu sama kuin merkkityypin < ja >? Mitkä merkit ovat yhtäsuuria? Yksi vai monta merkkiä? Ligatuurit: usean merkin sulautuminen yhdeksi, ß, ij, Ľ, ŋ, œ, ä Muutos pienten/isojen kirjainten välillä: ß SS Monta tapaa esittää merkki: ä (U+00E4) vs. ä = a (U+0061, U+0308) Tukeeko ohjelmointikieli montaa koodausta? Useita merkkityyppejä: hallintaongelmat Yksi merkkityyppi: valittava riittävän laaja (Unicode UCS4) Yhteensopivuus vanhojen kieliversioiden kanssa... 10
Lähdekoodin merkistökoodaus Ongelma: tekstiedoston merkkikoodausta ei (yleensä) ilmoiteta! Merkki/merkkijonoliteraalit vs. tunnisteet Ratkaisuyrityksiä: Koodaus määrätään kielessä (Java) Määrätään lähdekooditiedostossa (Python) Kerrotaan komentorivillä (C++-kääntäjät) Jätetään ottamatta kantaa (C++, monet muut) 11
Merkkijonotyypit Perinteisesti: jono/lista/taulukko merkkejä Tyypillisiä operaatioita: Indeksointi Iterointi (läpikäynti merkki merkiltä) Paloittelu Tulostus yms. Miten merkkijonon merkit koodataan? Merkkityypin ongelmat koskevat myös merkkijonoja 32 bittiä/merkki nelinkertaistaistaisi muistinkulutuksen (erit. välimuistin) 12
Unicode ja merkkijonot Merkkejä standardoitu nyt yli 109 000 (tilaa 1 114 112:lle) Merkeillä koodit U+000000 U+10FFFF Osa koodeista varattu erikoistarkoituksiin Käytännössä 32 bittiä/merkki (4 tavua) Merkkijonokoodaus UTF-32 (UCS4) Merkkijono on jono 32-bittisiä Unicode-merkkejä Helppo, vrt. perinteiset 8-bittiset merkkijonot Muistinkulutus suurta, bittejä tuhlaantuu paljon 13
Unicode ja merkkijonot UTF-16 Merkkijono koostuu 16-bittisistä tavupareista Unicode-merkit U+FFFF koodautuvat suoraan (suurin osa maailman kirjainmerkeistä) Loput merkit esitetään sijaismerkeillä (surrogates), kahdella 16-bittisellä tavuparilla (varatuilla alueilla, eivät voi mennä sekaisin kirjainmerkkien kanssa) Sijaismerkit koodattu niin, että parin 1. ja 2. merkit eivät voi mennä sekaisin (tunnistettavissa) Seuraus 1: Tavuparista näkee suoraan, onko se kirjainmerkki, sijaismerkkiparin alkuosa vai loppuosa. Seuraus 2: Yksi kirjain voi viedä yhden tai kaksi tavuparia!!! 14
Unicode ja merkkijonot UTF-8 Merkkijono koostuu 8-bittisistä tavuista Unicode-merkit U+7F suoraan (7-bittinen ASCII) Loput merkit U+7FF koodataan 2 tavulla (Eurooppa) Loput U+FFFF 3 tavulla (erikoismerkit, Aasia) Loput 4 tavulla (vanhat kielet, matematiikka,...) Seuraus: Yksi merkki voi viedä 1-4 tavua Monitavuisten merkkien tavut erotettavissa 1-tavuisista, monitavuisten 1. tavu erotettavissa muista Seuraus: tavujonoa voi selata, kunnes ollaan merkin alussa 15
Unicode-haasteet/ongelmat Indeksointi yms. taulukoksi tulkinta (UTF-8 ja UTF-16) Merkit eri kokoisia, s[i] ei palauta i+1:ttä merkkiä! Merkkijonon pituus ja taulukon koko eri asia Mahdollisuus indeksoida keskelle merkkiä Merkin korvaaminen toisella vaikeaa jos koko muuttuu Merkkijonoilla koodaaminen erilaista kuin ennen Unicode-iteraattori: palauttaa Unicode-merkkejä, siirtyy jonossa oikean verran eteenpäin 16
Unicode-haasteet/ongelmat Muistinkulutus kasvaa, välimuistia tuhlaantuu (erit. UTF-32) Aakkostus Numerokoodeihin perustuva aakkostus mahdotonta Yhtäsuuruusvertailu Merkkijonot normalisoitava ennen vertailua Konversiot eri koodauksien välillä (jos tuetaan useita) 17
Unicode joissain kielissä Java Merkkijonojen koodaus UTF-16 Lähdekoodin koodaus UTF-8 Python Merkkijonoja "raaka" ja Unicode Lähdekoodin koodaus ilmoitetaan koodissa C++ C++03: Ei määritelty (wchar_t ehkä UTF-32) C++11: Puutteellinen tuki UTF-8/16/32:lle Lähdekooditiedoston koodaus kääntäjäkohtainen 18
Luetellut tyypit Mahdolliset arvot määrittelevät tyypin Nimettyjen vakioiden kokoelma Suunnittelussa huomioitava: voiko lueteltu vakio esiintyä useammassa tyyppimäärittelyssä ja jos voi, niin miten tyyppitarkistukset tehdään tulkitaanko luetellut arvot automaattisesti kokonaisluvuiksi voidaanko muiden tyyppien arvot tulkita luetellun tyypin arvoksi Onko kaikki luettelon arvot annettava tyypin esittelyssä, vai voiko arvoja esitellä lisää muualla 19
Esimerkkejä luetelluista tyypeistä Pascal: C++: Java: type colors = ( red, blue, green, yellow, black ); enum colors { red, blue, green, yellow, black }; colors mycolor = blue;... mycolor++; /* sallittu */ mycolor = 4; /* kielletty */ class colors { public final int red = 0; public final int blue = 1;... } 20
Alityypit (Ada) subtype SmallInt is Integer range -100..100; subtype MicroInt is SmallInt range -10..10; Arvorajoite, ei itsenäinen tyyppi Nimetyn alityypin etuja (verrattuna nimettömään): rajoitetta ei tarvitse toistaa, jos alityyppiä käytetään useassa esittelyssä rajoitteen muuttuessa riittää tehdä korjaus yhteen paikkaan kuvaava tunnus parantaa luettavuutta toteutus tulee tehokkaammaksi X: Integer;... subtype R is Integer range 1..X; 21
Johdetut tyypit (Ada) Uusi tyyppi yhteensopimaton muiden tyyppien kanssa type Color is ( Red, Yellow, Green, Black, White ); type TrafficLight is new Color range Red..Green; c: Color; t: TrafficLight;... c := Red; t := Red;... if Red < Green then... -- kielletty if Color (Red) < Green then... -- sallittu 22
Taulukot Vektorit Matriisit Samaa tyyppiä olevien tietoalkioiden järjestetty kokonaisuus Taulukkotyypin määrittelyyn kuuluu: taulukkotyypin nimi (tai nimetön tyyppi) alkioiden tyyppi indeksin tyyppi (arvoalue = taulukon koko) Indeksointi taulukoiden perusoperaatio hakasulut tai kaarisulut 23
Taulukoiden suunnittelu Mitkä tyypit voivat olla taulukon indeksityyppeinä? kokonaislukujen osaväli merkkityyppi, lueteltu tyyppi, looginen tyyppi Tarkistetaanko taulukkoviittauksissa indeksirajat? Milloin indeksirajat sidotaan? Milloin taulukolle varataan tilaa? Voidaanko taulukko alustaa tilan varaamisen yhteydessä? Voidaanko taulukosta ottaa viipaleita? 24
Assosiatiiviset taulukot Tavanomaiset taulukot alkioilla implisiittinen järjestys taulukon indeksejä ei talleteta Assosiatiiviset taulukot järjestämätön joukko alkioita kuhunkin alkioon liittyy avain, jonka avulla alkioon viitataan myös avainten arvot talletetaan esim. Perl 25
Tietueet Heterogeeninen rakenne komponentit (kentät) voivat olla eri tyyppiä komponentteihin viitataan nimellä Komponentit (kentät) voivat olla tietueita hierarkkinen puurakenne Tietuetyypin määrittelyyn kuuluu: tyypin nimi kenttien nimet ja tyypit Operaatioita: sijoitus yhtäsuuruus- ja erisuuruusvertailu Record Name Type Offset... Name Type Offset Address 1. kenttä n. kenttä 26
Ada: Tietueen operaatiot Tietueen kenttään viittaaminen Arvojen sijoittaminen kenttiin Yleisin tapa: tietue.kenttä Algol68: kenttä ( tietue ) henkilo := ( etunimi => Matti, sukunimi => Virtanen ); henkilo := ( Matti, Virtanen ); Pascalin with-lause: type complex = record re: real; im: real; end; var x: complex; with x do begin re := 1.5; im := 2.0; end; 27
Vaihtelevat tietueet C: Ada: typedef union { char car [ ]; int bicycles; } garage; type owner_type is ( car_freak, bike_freak ); type garage ( owner: owner_type ) is record case owner is when car_freak => car: String; when bike_freak => bicycles: Natural; end case; end record; 28
Vaihtelevien tietueiden turvallisuus Ada: C: garage my_garage; my_garage.car = Trabant ; printf ( %d\n, my_garage.bicycles ); my_garage: garage; my_garage := ( owner => car_freak, car => Porsche ); -- could not pay for it my_garage.car := VW ; -- changed my way of life my_garage := ( owner => bike_freak, bicycles => 8 ); 29
Vaihtelevien tietueiden turvallisuus Haskell: data Garage = CarFreak String BikeFreak Int sillyfunc (CarFreak type) = length type sillyfunc (BikeFreak num) = num gar1 = (CarFreak "BMW") gar2 = (BikeFreak 8) sillyfunc gar1 -- tulos: 3 sillyfunc gar2 -- tulos: 8 30
Ada-koodia: Vaihtelevan tietueen toteutus type Shape is ( Circle, Triangle, Rectangle ); type Colors is ( Red, Green, Blue ); type Figure ( Form: Shape ) is record Filled: Boolean; Color: Colors; case Form is when Circle => Diameter: Float; when Triangle => LeftSide: Integer; RightSide: Integer; Angle: Float; when Rectangle => Side1: Integer; Side2: Integer; end case; end record; Rectangle: Side1 Side2 Circle: Diameter Filled Color Form Triangle: LeftSide RightSide Angle 31
Osoittimet kekodynaamisia muuttujia Osoitintyypin arvoalue arvona muistiosoite erikoisarvo (nil, null) Mahdollisia operaatioita arvon asettaminen yhtäsuuruuden vertailu viittauksen arvon haku (dereference) Osoitinaritmetiikka (C) halutun vakioarvon (esim. 0xCE00) asettaminen osoittimeen kokonaisluvun lisääminen osoittimeen kahden osoittimen välinen erotus 32
Osoittimien etuja Muistin varaus ja vapautus ovat joustavampia (ei sidottu ohjelman rakenteeseen) Samaan muistipaikkaan voidaan viitata useilla osoittimilla Muistipaikan sisältöä ei tarvitse kopioida esim. isot parametrit Osoittimet ovat luonnollisin tapa rekursiivisten tietorakenteiden toteuttamiseen 33
Osoittimien ongelmia Saavuttamaton tietoalkio eli roska (garbage) dynaamiselle muuttujalle varataan tilaa osoittimen kautta tilaa varataan uudestaan saman osoittimen kautta Roikkuva osoitin (dangling reference) muuttujalle varataan tilaa yhden osoittimen kautta osoittimen arvo sijoitetaan toiselle osoittimelle tila vapautetaan jommankumman osoittimen kautta toinen osoitin jää osoittamaan vapautettua muistitilaa New ( p );... New ( p ); Pascalkoodia New ( p1 ); p2 = p1; Dispose ( p1 ); 34
Roikkuvien osoittimien käsittely Hautakivimenetelmä jokaisella dynaamisella muuttujalla on erityinen solu (hautakivi), joka myös itse on osoitin dynaamiseen muuttujaan varsinaiset osoitinmuuttujat osoittavat hautakiveen kun muuttuja tuhotaan, hautakivi jää, mutta osoittaa nil:iin Lukot ja avaimet osoittimien arvoihin liitetään kokonaisluku (avain) dynaamisiin muuttujiin liitetään kokonaisluku (lukko) tilaa varattaessa luodaan lukolle arvo, joka tallennetaan sekä lukko- että avainkenttään viittauksissa tarkistetaan, että lukko ja avain täsmäävät tilan vapauttamisessa lukon arvo muutetaan laittomaksi 35
Adan osoittimet Roikkuvien osoittimien eliminointi ei operaatiota dynaamisen muuttujan vapauttamiseen paitsi Unchecked_Deallocation, jonka käyttöä ei suositella osoitintyypin esittely kokoelman syntyminen (joukko dynaamisia muuttujia) esittelyn sisältävä lohko päättyminen kokoelma lakkaa olemasta type IntPoint1 is access Integer; R1: IntPoint1 := new Integer; type IntPoint2 is access Integer; R2: IntPoint2;... R2 := R1; -- tyyppivirhe 36
Osoittimet C:ssä ja C++:ssa Roikkuvat osoittimet mahdollisia Osoitinaritmetiikka Osoittimilla ja taulukoilla läheinen yhteys int list [ 10 ]; int *ptr; ptr = list; * ( ptr + 1 ) list [ 1 ] * ( ptr + index ) list [ index ]; ptr [ index ] list [ index ]; 37
Viitetyypit (reference type) Vakio-osoitin viittaa aina johonkin dataan tunnusta edeltää & (C++:ssa) Yleisin käyttö muodollisena parametrina mahdollistaa kaksisuuntaisen tiedonvälityksen C++-koodia int a = 0; int &a_r = a; C++-koodia void duplicate ( int &i ) { i = i * 2; }... int x = 2; duplicate ( x ); // x 4 C-koodia (ei viitetyyppiä) void duplicate ( int *i ) { *i = *i * 2; }... int x = 2; duplicate ( &x ); // x 384
Modula-2:ssa ja Pascalissa Operaatioita (binaarisia) unioni ( + ) leikkaus ( * ) erotus ( - ) symmetrinen erotus ( / ) Joukkotyypit Modula-2-koodia: TYPE Digit = [ 0..9 ]; DigitSet = SET OF Digit; { 0, 3, 5 } 1 0 0 1 0 1 0 0 0 0 alkiot, jotka kuuluvat vain toiseen joukkoon, eivät molempiin sisältyminen Toteutus joukon (bittivektorin) täytyy mahtua yhteen sanaan (esim. 16, 32) operaatiot bittioperaatioita esim. unioni vastaa loogista or-operaatiota 39
Tyyppiekvivalenssi Nimiekvivalenssi tyypin tunnus on tyypin identifioiva nimi eri nimiset tyypit ovat eri tyyppejä kukin nimettömän tyypin esittely esittelee erillisen tyypin Rakenne-ekvivalenssi tyypin tunnus on tyyppimäärittelyn lyhenne tyypit ovat samoja, jos niillä on sama rakenne ja rakenteen komponenteilla on sama tyyppi tai identtinen määrittely Molempia voidaan soveltaa sekä skalaarityyppeihin että rakenteisiin tyyppeihin typedef int paino; C-koodia: typedef int pituus; 40
Lisää esimerkkijä tyyppien samuusongelmista Pascal-koodia: X, Y: array [ 1..10 ] of Integer; type T = array [ 1..10 ] of Integer; U = T; var X: T; Y: U; X: array [ 1..10 ] of Integer; Y: array [ 2..11 ] of Integer; 41
Rakenne-ekvivalenssin selvittäminen 1. Ota käsiteltäväksi molemmat tyyppimäärittelyt. 2. Korvaa kummassakin tyyppimäärittelyssä jokainen tyypin tunnus vastaavalla tyyppimäärittelyllä. 3. Toista askelta 2 kunnes tyyppimäärittelyissä ei ole enää tyyppien tunnuksia (paitsi standardityyppien) 4. Vertaa tuloksena saatuja määrittelyjä tekstinä. Jos ne ovat identtisiä jonoja kielen alkioita, tyypit ovat samat. 42
Nimiekvivalenssin etuja Helpompi toteuttaa Parempi tyyppisuojaus Tyyppien abstraktisuus Ohjelman luettavuuden ja ymmärrettävyyden paraneminen (nimetyt tyypit) 43
Tyyppiekvivalenssi ohjelmointikielissä Nimiekvivalenssi Ada poikkeuksena nimetyt alityypit C++ ja Java Rakenne-ekvivalenssi Algol68 ja Modula-3 C paitsi tietuetyypit (struct, union) typedef-määrittely ei luo uutta tyyppiä 44