Helsingin yliopisto / tietojenkäsittelytieteen laitos / Ohjelmointitekniikka (Scala) / © Arto Wikla 2016

2 Alkeita: tulkki ja lauseita

Muutettu viimeksi 23.2.2016 / Sivu luotu 9.3.2010 / [oppikirjan esimerkit] / [Scala]

Sivun sisältöä:

Tulkissa

$ scala
Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_72).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 1 + 2
res0: Int = 3
scala> res0 * 3
res1: Int = 9
Tulkki siis laittaa nuo arvot itse nimeämiinsä systeemivakioihin res0, res1, res2, ...

scala> println("Hello, world!")
Hello, world!
Metodikutsut ja muut algoritminpalaset suoritetaan sellaisinaan.

scala> val msg = "Hello, world!"
msg: java.lang.String = Hello, world!

scala> val msg2: java.lang.String = "Hello again, world!"
msg2: java.lang.String = Hello again, world!
Toki tyypin saa määritelläkin kuten yllä: val msg2: java.lang.String = ...
scala>  println(msg)
Hello, world!
Nimetyt vakiot siis ovat vakioita:
scala> msg = "Goodbye cruel world!"

<console>:5: error: reassignment to val
       msg = "Goodbye cruel world!"
           ^
Muuttujat määritellään var-ilmauksella:
scala> var muu = 666
muu: Int = 666

scala> println(muu)
666

scala> muu = 14
muu: Int = 14

scala> println(muu)
14

Rakenteen jäädessä kesken tulkki antaa uuden rivin:
scala> val multiLine =
     | "This is the next line."
multiLine: java.lang.String = This is the next line.
Tyypillisesti näin käy funktion määrittelyssä:
scala> def max(x: Int, y: Int): Int = {
     | if (x > y) x
     | else y
     | }
max: (x: Int, y: Int)Int
Jos funktion runko muodostuu yhdestä lauseesta, aaltosulkeet saa jättää pois:
scala> def max2(x: Int, y: Int) = if (x > y) x else y
max2: (x: Int, y: Int)Int

scala> max(3, 5)
res5: Int = 5
Näiden kahden funktion tyyppi siis on (Int,Int)—>Int.

Huom: Funktion arvon tyyppi ilmaistaan otsikossa, ellei kääntäjä osaa sitä päätellä. Ja palautettavan arvon ilmaisemiseen riittää algoritmin suorituspolun viimeinen ilmaus, mitään returneja ei tarvita. Toisaalta return-lause on myös käytettävissä, jos halutaan algoritmiin useita erillisiä lopetuskohtia. Tätä ei kuitenkaan Scala-maailmassa pidetä tyylikkäänä eikä suotavana!

Huom: Koska "kaikella on arvo" ja arvo on lause-/lausekejonon viimeinen arvo, seuraavanlainen sivuvaikutuksilla pelleilykin on mahdollista:

scala> val x = {println("böö"); 78}
böö
x: Int = 78

Huom: Funktion muodollisten parametrien tyyppi annetaan aina!

"Void-metodin" tyyppi on Unit:

scala> def greet() = println("Hello, world!")
greet: ()Unit
Parametrittoman funktion kutsussa tyhjä parametrilista ei ole tarpeen:
scala> greet
Hello, world!

scala> greet()
Hello, world!

scala> sys exit   // tai toki myös sys.exit tai sys.exit()
$

Huom: Ilman yhtäsuuruusmerkkiä funktion tyyppi on Unit:

scala> def f(a:Int, b:Int) {a-b}
f: (a: Int, b: Int)Unit

scala>  def f(a:Int, b:Int) = {a-b}
f: (a: Int, b: Int)Int

Huom: Kutsussa voi nimetä, mitkä muodolliset parametrit saavat arvokseen todelliset parametrit:

scala> def f(a:Int, b:Int) = {a-b}
f: (a: Int, b: Int)Int

scala> f(1,2)
res3: Int = -1

scala> f(b=1, a=2)
res4: Int = 1

Komentoriviparametri skriptissä

Skriptille, so. Scala-kieliselle tekstitiedostolle, joka suoritetaan komennolla scala, voi antaa komentoriviparametreja. Ne löytyvät taulukosta args
  // Say hello to the first argument
  println("Hello, "+ args(0) +"!")
Huom: Taulukon indeksit Scalassa kirjoitetaan kaarisulkeisiin!

Valintaa ja toistoa, funktioparametreja

[oppikirjan esimerkit]

Tuttua ja turvallista:

  var i = 0
  while (i < args.length) {
    println(args(i))
    i += 1
  }
Ja iffitkin peliin:
  var i = 0
  while (i < args.length) {
    if (i != 0)
      print(" ")
    print(args(i))
    i += 1
  }
  println()
Huom: Scalassa ei ole operaatioita ++i ja i++. Ja hyvä niin! Ehtojen ympärillä on Javan tapaan oltava kaarisulkeet.

Skaalautuvalla Scalalla tuommoinen perinneohjelmointi toki onnistuu, ohjelmointi, jossa peräkkäin suoritettavin operaatioin muutetaan ja tutkitaan muuttujien arvoja eli ohjelman tilaa (state).

Mutta Scalalla hommia voi hoidella korkeammallakin abstraktiotasolla. Komentoriviparametrit voi tulostaa vaikka näin:

  args.foreach(arg => println(arg))

Näyttää siistiltä, mutta mitä tässä oikeastaan sanotaan?

Esimerkki: Laaditaan funktio, joka saa parametrina kaksi kokonaislukua ja funktion, joka kuvaa kaksi lukua kolmanneksi. Laadittava funktio soveltaa saamaansa funktiota parametrilukuihin:

  def koristele(x: Int, y: Int, arvo: (Int, Int) => Int) =
    println("**** " + arvo(x, y) + " ****")
Ja näin kätevästi sitä sitten voi käyttää:
  koristele(5, 9, (x: Int, y: Int)  => x + y)  // **** 14 ****
  koristele(5, 9, (x: Int, y: Int)  => x * y)  // **** 45 ****

  def erotus(a: Int, b: Int) = a-b
  koristele(5, 9, erotus)                      // **** -4 ****
Kuten näkyy, todellisena funktioparametrina voidaan antaa funktioliteraalien lisäksi myös nimettyjä funktioita!