Kirjautuminen

Haku

Tehtävät

Opasarkisto: Pascal-ohjelmointi: Osa 2 - Vakiot, muuttujat ja tietueet

  1. Osa 1 - Mistä Pascal alkaa?
  2. Osa 2 - Vakiot, muuttujat ja tietueet
  3. Osa 3 - Silmukat ja ehtolauseet
  4. Osa 4 - Tiedostot
  5. Osa 5 - Funktiot, aliohjelmat ja omat moduulit

Kirjoittaja: Metabolix (2004).

⚠ Huomio! Tämä opas on vanhentunut. Oppaan sisältöön ei voi enää luottaa. Opas on säilytetty vain sen historiallisen arvon vuoksi. ⚠


Pascal on hyvin tarkka kieli muuttujien tyypin suhteen. Kun käyttää Pascalia, ei voi laittaa merkkijonomuuttujan arvoksi lukua 10, koska 10 on luku eikä merkkijono. Tämä ominaisuus tekee Pascalista erittäin mukavan kielen ohjelmoida: sen sijaan, että kääntäjä kirjoittaisi huolella piilotettuun lokitiedostoon pienen varoituksen, se ilmoittaa kiertelemättä, että näin ei voi tehdä.

Muuttujien, vakioiden ja tietueiden nimissä saa käyttää seuraavia merkkejä: 'a' .. 'z', 'A' .. 'Z', '_', '0' .. '9'. Nimi ei saa alkaa numerolla. Kirjainten koolla ei ole käytännön merkitystä; 'etana' on sama kuin 'EtAnA'.

Vakiot

Vakioista on hyvä aloittaa, koska ne ovat yksinkertaisia. Vakio voi olla mikä tahansa tavallinen muuttujatyyppi tai lueteltu tyyppi, mutta se ei voi olla tietue. Vakion tyyppiä ei tarvitse ilmoittaa, vaan kääntäjä päättelee sen annetusta arvosta.

Miksei tätä arvoa voi sitten kirjoittaa joka kerta uudestaan? Kyllähän sen toki voi... Huonoja puolia on kaksi:

  1. Kuvittelepa tehneesi mutkikas matemaattinen sovellus joka laskee erilaisia asioita pyöreistä kappaleista. Sitten, eräänä kauniina päivänä saat kuulla, että matemaattinen vakio pii on tähän asti laskettu aivan väärin. Siispä sinä, joka et omista korvaustoiminnolla varustettua tekstieditoria, etsit ohjelmastasi kaikki ne tuhat kohtaa joihin olet kirjoittanut piin likiarvon ja muutat niihin jokaiseen uuden arvon. Olisitko mahdollisesti päässyt helpommalla kirjoittamalla jokaiseen alun perinkin vain 'pii' ja määrittämällä ohjelman alkuun piin arvon?
  2. Vakioiden käyttö selkeyttää koodia huomattavasti. Vakio, jonka nimestä näkee sen tarkoituksen, säästää paljon vaivaa koodia kommentoitaessa.

Pascalissa vakioiden määrittely tapahtuu sanan const avulla ja itse ohjelmakoodin ulkopuolella eli ennen sanaa begin. Se voi olla vaikkapa tällainen:

const
  OmaTeksti = 'Tämä on tekstivakio!';
  Pii = 3.14159265358979;
  JokuLukuVaan = 3125694;
  Heksadesimaaliluku = $1F6bC;

Erilaisia muuttujia

Muuttujia on useita erilaisia. Tavallisimpia ovat kokonaisluvut, liukuluvut (desimaaliluvut) ja merkkijonot sekä Boolean, jonka arvo on True tai False (tosi tai epätosi).

Kokonaislukutyypit suurimmasta pienimpään ovat:

NimiLukualueLukualueMuistinvaraus (tavua)
Int64-9223372036854775808 .. 9223372036854775807-263 .. 263 - 18
LongWord0 .. 42949671950 .. 232 - 14
Longint-2147483648 .. 2147483647-231 .. 231 - 14
Integer (Yleensä)-2147483648 .. 2147483647-231 .. 231 - 14
Word0 .. 655350 .. 216 - 12
Smallint-32768 .. 32767-215 .. 215 - 12
Integer (Vanhoissa kääntäjissä)-32768 .. 32767-215 .. 215 - 12
Byte0 .. 2550 .. 28 - 11
Shortint-128 .. 127-27 .. 27 - 11

Normaalit liukulukutyypit ovat:

NimiLukualueMuistinvaraus (tavua)Merkitseviä numeroita
Single1.5 * 10-45 .. 3.4 * 103847 .. 8
Double5.0 * 10-324 .. 1.7 * 10308815 .. 16

Merkkijonotyypit ovat:

NimiMaksimipituusMuistinvaraus
ShortString255 merkkiä2 .. 256 tavua
AnsiString4294967295 merkkiä4 tavua .. 2 gigatavua
String (normaalisti)4294967295 merkkiä4 tavua .. 2 gigatavua
WideString2147483647 merkkiä4 tavua .. 2 gigatavua

Lisäksi Pascalissa voi määrittää lueteltuja tyyppejä. Nämä laitetaan tietueiden tapaan kohtaan type, minkä jälkeen ne ovat käytettävissä samoin kuin muutkin muuttujatyypit. Määritys voi olla esimerkiksi tällainen:

type
  Ase = (Puukko, Kirves, Pistooli, Haulikko, AK_47);

Aseet on numeroitu järjestyksessä, alkaen nollasta. Voit siis suorittaa vertailuja samoin kuin luvuilla, esimerkiksi näin:

if OmaAse < Haulikko then OstaIsompi(TosiIso, VaikkaVelaksi);
if Vihollinen.Ase > Kirves then JuoksePakoon(Kauas, Kovaa);

Muuttujien käyttöä

Kaikkein ensimmäiseksi muuttujat pitää määritellä. Pascalissa tämä tapahtuu sanan var avulla ja itse ohjelmakoodin ulkopuolella eli ennen sanaa begin:

var
  H455U_Luku: Integer;
  __Liukuluku: Single;
  OmaTeksti: String;

Muuttujia voi käyttää vasta itse ohjelmakoodin alettua eli sanan begin jälkeen.

Muuttujalle annetaan arvo merkinnällä :=

H455U_Luku := 7;
__Liukuluku := 9.99999;
OmaTeksti := 'Hello, World!';

Muuttujalle annetava arvo voi olla myös lauseke tai toinen muuttuja.
Lausekkeissa käytetään merkkejä +, -, * ja /. div palauttaa jakolaskun kokonaisosan ja mod jakojäännöksen. Jälkimmäiset toimivat vain kokonaisluvuilla.

H455U_Luku := 100 mod 3; { 1 }
H455U_Luku := 100 div 3; { 33 }
__Liukuluku := 9.99 + Luku; { 9.99 }
__Liukuluku := 9.99 * Luku; { 0 }
__Liukuluku := 4 / (Luku + 5); { 0.8 }
__Liukuluku := -__Liukuluku + 1; { -0.8 + 1 = 0.2 }
OmaTeksti := 'Pas' + 'cal' + ' on' + ' ki' + 'va' + ' kie' + 'li.'; { 'Pascal on kiva kieli.' }

Koska kokonaisluvun arvoksi ei voi antaa liukulukua, täytyy liukuluku katkaista trunc-funktiolla (truncate). Trunc ei pyöristä lukua, mutta pyöristetyn arvon saa lisäämällä lukuun ensin arvon 0.5. Luvun itseisarvon taas saa funktiolla abs.

H455U_Luku := trunc(9.999); { 9 }
H455U_Luku := trunc(9.999 + 0.5); { 10 }
H455U_Luku := trunc(abs(-1.23)); { 1 }

Lisäksi kokonaisluvuille toimivat bittioperaattorit and, or, xor, not, shr ja shl. Niitä käytetään aivan samoin kuin tavallisia laskutoimituksia. Tarkemmat tiedot löydät Pascal-hakemistosta.

Joukot

Joukot ovat juuri sitä mitä nimi sanoo: joukko tietyn tyyppisiä muuttujia. Joukon voi muodostaa mistä tahansa enintään 256 erilaista vaihtoehtoa sisältävästä muuttujatyypistä. Tämä ehto rajaakin hyvin vahvasti joukkojen käyttöä. Joukkovakiot merkitään hakasulkuihin ja joukkoja yhdistellään plus- ja miinusmerkeillä. Tietyn arvon olemassaolo selviää in-operaatiolla. Yleensä suurimman hyödyn joukoista saa juuri lueteltujen tyyppien kanssa.

program AseSet;
type
  Ase = (Puukko, Kirves, Pistooli, Haulikko, AK_47);
var
  Varustus: set of Ase;

begin
  { Varustuksessa olkoot puukko ja kirves }
  Varustus := [Puukko, Kirves];

  { Otetaan puukko pois ja AK_47 tilalle }
  Varustus := Varustus - [Puukko] + [AK_47];

  { Onko meillä nyt haulikko? }
  if Haulikko in Varustus then
    Writeln('Haulikko on.');
end.

Taulukot

Taulukko voi nimestään huolimatta olla juuri niin moniulotteinen kuin tarve vaatii. Taulukko määritetään samalla lailla kuin muutkin muuttujat. Määrittelyssä käytetään sanaa array, jonka voi kukin kirjoittaa halunsa mukaan isolla tai pienellä.

var
  Taulu: Array [1 .. 5] of Integer;
  Taulu2D: Array [1 .. 5, 3 .. 8] of Integer;
  Lippaan_Koko: Array [Ase] of Integer;

Taulukkoa käytettäessä sen alkioon pääsee käsiksi kirjoittamalla hakasulkuihin sen indeksin tai muun tunnisteen:

Taulu[1] := 10;
Taulu2D[1, 5] := 17;
Lippaan_Koko[Haulikko] := 1;
Taulu2D[Taulu[1], Taulu[2]] := 10; { Indeksiksi kelpaavat muuttujatkin. }

Normaalisti taulukon koko määritellään heti, eli taulukko on staattinen. Tällöin taulukkoa voi käyttää heti. Ainakin Delphi tarjoaa toisenkin tavan: koon voi määritellä vasta myöhemmin (dynaaminen taulukko), jolloin taulukko toimii kaikin puolin hieman eri lailla. Tästä on pieni esimerkki seuraavassa.

Yleinen esimerkki opitusta

program YleinenEsimerkki;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  { Siinä missä Byte voi olla väliltä 0 .. 255,
    Hedelma voi olla väliltä Omena .. Erikoishedelma }
  Hedelma = (Omena, Banaani, Paaryna, Erikoishedelma);
  Planeetat = (Merkurius, Venus, Maa, Mars, Jupiter, Saturnus, Uranus, Neptunus, Pluto);
  Lampoalue = (Trooppinen, Lauhkea, Jaatikko);

var
  Staattinen: array [Lampoalue, Planeetat] of set of Hedelma;
  { Staattisesta taulukosta tulee kaksiulotteinen,
    koska siihen on määritetty kaksi pituutta. }

  Dynaaminen: array of array of Integer;
  { Dynaamisesta taulukosta tulee kaksiulotteinen,
    koska "array of" lukee kaksi kertaa. Tämä ei
    siis toimi kuin Object Pascalissa. }

  Palsta: Integer;

const
  Pituus = 5;
  Leveys = 10;

begin
  { Asetetaan Dynaamisen taulukon pituudeksi Pituus.
    Koska numerot alkavat nollasta, tulee taulukosta
    kooltaan 0 .. (Pituus - 1) eli 0 .. 4 }
  SetLength(Dynaaminen, Pituus);

  { Asetetaan jokaisen palstan pituudeksi Leveys.
    Koska numerot alkavat yhä nollasta, tulee kunkin
    palstan leveydeksi 0 .. (Leveys - 1) eli 0 .. 9 }
  for Palsta := 0 to Pituus - 1 do
    SetLength(Dynaaminen[Palsta], Leveys);

  { Nyt Dynaamisen eri kohdille voidaan antaa arvoja.
    Koordinaatit laitetaan hakasulkuihin. }

  Dynaaminen[0][3] := 10;

  { Staattiselle ei tarvitse tehdä mitään ennen käyttöä.
    Kukin kohta on tyyppiä "set of Hedelma", jolloin
    joka kohdassa voi olla hedelmistä koottu joukko.
    Myös osoitustapa on erilainen kuin Dynaamisessa.

    Kaikki joukot merkitään hakasulkuihin.
    Joukkojen yhteen- ja vähenyslaskuissa joukkojen tulee olla samaa tyyppiä.
    Yksittäisten jäsentenkin tulee muodostaa joukko }

  Staattinen[Trooppinen, Maa] := [Omena, Banaani, Paaryna, Erikoishedelma];
  Staattinen[Lauhkea, Maa] := Staattinen[Trooppinen, Maa] - [Banaani, Erikoishedelma];
  Staattinen[Trooppinen, Mars] := [Erikoishedelma];
end.

Käytetystä for .. to .. do -lauseesta kerrotaan lisää seuraavassa osassa.

Tietueet

Tietueet ovat muuttujia, jotka koostuvat muista muuttujista. Esimerkiksi tietue "TAuto" voisi sisältää osat "KuljettuMatka" ja "Huippunopeus" jotka olisivat vaikkapa kokonaislukuja. Muuttujassa "X", jonka tyyppi on "TAuto", pääsisimme näihin käsiksi kirjoittamalla "X.KuljettuMatka" tai "X.Huippunopeus".

Nimen edessä on T, jotta tunnistamme myöhemminkin koodissa nimen tietuetyypiksi. Merkintä ei ole pakollinen, mutta se selkeyttää koodia.


Ohjelman muistinkäytön pienentämiseksi kannattaa tietueesta jättää kaikki tarpeeton pois. Turha uhrata auton moottorille tilaa, jos sitä ei tarvitse. Epäolennaisia ei turhaan tarvitse muistaa.

Tietueet määritellään yleensä ohjelman alkuun, jotta ne ovat käytössä koko ojelman ajan. Määrittely voi tapahtua esimerkiksi näin:

type
  TBensatankki = record
    MaxLitraa: Integer;
    LitraaJaljella: Integer;
  end;
  TAuto = record
    KuljettuMatka: Integer;
    Huippunopeus: Integer;
    Tankki: TBensatankki;
  end;

Tämän jälkeen voit määritellä tekemääsi tyyppiä olevan muuttujan ja käyttää sitä ohjelmassasi. Lisäksi on mahdollista määritellä tyyppi vasta muuttujan määrittelyn yhteydessä:

var
  { Käytetään aiempaa tyyppiä, TAuto }
  OmaAuto: TAuto;

  { Määritellään, millainen tyyppi naapuri on }
  Naapuri: record
    Auto: TAuto;
    Rahat: Integer;
  end;

begin
  OmaAuto.Tankki.MaxLitraa := 50;
  OmaAuto.Tankki.LitraaJaljella := 0;

  { Viedään naapurilta bensat: }
  OmaAuto.Tankki.LitraaJaljella := Naapuri.Auto.Tankki.LitraaJaljella;
  Naapuri.Auto.Tankki.LitraaJaljella := 0;
end.

Lauri Kenttä, 17.8.2004


Kommentit

Koipio-ohjelma [21.09.2004 18:06:41]

#

Ahhaaa! Oppaan osat 2 ja 3 ovat menneet sekaisin!

hunajavohveli [21.09.2004 20:38:01]

#

Dynaaminen: array of array of Integer;
Turbo Pascal ei hyväksy tuota. "Error "[" or "(." expected". Onko siinä jotain eroa muihin kääntäjiin verrattuna?
Vai riippuuko jotenkin tuosta SysUtilsista? Sitä ei näytä löytyvän.

Metabolix [21.09.2004 23:32:09]

#

Hienoa, että tämäkin puute oppaissa tuli ilmi. Oppaat on nyt korjattu noilta osin.
Kyseessä on siis vain Delphin ominaisuus. Mikäli näitä ilmenee lisää, niistä voi ilmoittaa vaikka sähköpostilla, mutta niihin, jotka huomasin, lisäsin asiasta maininnan.

BlueByte [16.10.2004 13:54:56]

#

melko oudosti noi taulukot esittelet

BlueByte [16.10.2004 13:55:37]

#

muuten noi dynaamiset taulukot ei toiminut dev-pascalissa (kääntäjä freepascal) kun yritin kääntää :(

muumitalo [19.01.2005 09:44:42]

#

tuleepas ihan vanhat ajat mieleen :)

NanoSoft [31.03.2006 00:22:11]

#

voiko pascalissa muuttuja sisältää muita muuttujia ja mikä niiden "yhdistämismerkki" on? (php:ssä ., vb:ssä & jne.)

Metabolix [01.04.2006 08:44:54]

#

Kuten esimerkeistä näkyy (kannattaa lukea opas ennen kommentointia ^^), tekstinpätkiä yhdistellään plus-merkillä.

Luvut ja teksti ovat kaksi eri asiaa, ja niiden keskiset muunnokset eivät ole yhtä suoraviivaisia (näinhän se tapaa useimmissa kielissä olla). Tyypillisesti SysUtils-moduuli sisältää funktiot IntToStr ja StrToInt kokonaislukujen muunnoksiin sekä vastaavat funktiot StrToFloat ja FloatToStr liukulukutyypeille.

Mitä mahdat tarkoittaa muuttujilla, jotka sisältävät muita muuttujia? Mitään PHP-tyylistä järjestelmää ei ole ($muuttuja = "toinen" => $$muuttuja = $toinen), koska muuttujien nimet tapaavat hävitä käännösvaiheessa. Osoittimista kerrotaan myöhemmin oppaissa, kunhan pääset sinne asti :)

Juhko [13.02.2009 21:04:04]

#

Jätkä, kuin vanha sä olit ku kirjotit nää? o_O

Nimittäin aikas hyviä oppaita.

Metabolix [14.02.2009 12:13:54]

#

Juhko kirjoitti:

Jätkä, kuin vanha sä olit ku kirjotit nää? o_O

Sen voit laskea profiilistani.

Kyllähän näistä perusasiat selviävät, mutta totta puhuen olen myöhemmin oppinut paljon uusiakin asioita. Vanhoja oppaita vain on hirveän hankala täydentää luontevasti.

Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea kirjoitusohjeet.
Tietoa sivustosta