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

8 Funktiot ja sulkeumat

Muutettu viimeksi 4.3.2016 / Sivu luotu 29.3.2010 / [oppikirjan esimerkit] / [Scala]

Sivun sisältöä:

Metodi ja funktio

Scalassa kaikkia aliohjelmia on tapana nimittää funktioiksi vaikka ne eivät välttämättä aina olekaan funktioita matematiikan mielessä. Kun funktiota käytetään olio-ohjelmoinnin "aksessorina" tai jos funktio jossakin muussa mielessä(?) on olion tai luokan "jäsen", sitä kutsutaan perinteisesti myös metodiksi, "menetelmäksi" käsitellä jotakin oliota tai luokan ilmentymää.

Esimerkki klassisesta ohjelmointityylistä:

Laaditaan ainokaisena "kirjasto", jossa on julkisia työkalufunktioita ja niiden yksityisiä apufunktioita:

  import scala.io.Source
  object LongLines {
    def processFile(filename: String, width: Int) {
      val source = Source.fromFile(filename)
      for (line <- source.getLines) 
        processLine(filename, width, line)
    }
    private def processLine(filename: String, width: Int, line: String) {
      if (line.length > width)
        println(filename +": "+ line)
    }
  }
Laaditaan sovellus, joka käyttää kirjastoa. Tehtävänä on tulostaa annetuista tiedostoista kaikki rivit, joiden pituus ylittää annetun pituuden. Komentoriviparametrit annetaan järjestyksessä pituus, tiedosto1, tiedosto2, ...
  object FindLongLines {
    def main(args: Array[String]) {
      val width = args(0).toInt
      for (arg <- args.drop(1))
        LongLines.processFile(arg, width)
    } 
  } 
Käyttöesimerkki:
$ scala FindLongLines 72 Rautatie.txt LongLines.scala 
Rautatie.txt: tullut. Vaan mitenkä? ... sitä Matti ei osannut itselleen selittää ... ja
Rautatie.txt: kotoapäinhän sitä tullaan ... hyvää iltaa, ryökkynä ... terve, terve! ...
Rautatie.txt: --Monienkin pyörien päällä kulki ... ja kun me seistiin siinä sen huoneen
Rautatie.txt: --Vähät minä siitä, jos en näekään ... ja niinpä tuo jalkojakin pakottaa,
Rautatie.txt: oikeaan käteen ja tämä tie viepi suoraan kirkolle ... on siinä punaiseksi
Rautatie.txt: --Herra i--ihme! ... katso! kun on ... sehän on koreampi kuin pappilan...
Rautatie.txt: --Elä he--ele--!... Siitäkö se nyt!... Hyvä isä siunatkoon ... siinäkö se
Rautatie.txt: 1.C.  The Project Gutenberg Literary Archive Foundation ("the Foundation"
Rautatie.txt: located in the United States, we do not claim a right to prevent you from
Rautatie.txt: freely sharing Project Gutenberg-tm works in compliance with the terms of
Rautatie.txt: posted on the official Project Gutenberg-tm web site (www.gutenberg.net),
LongLines.scala:     private def processLine(filename: String, width: Int, line: String) {

Paikalliset funktiot

Edellisessä esimerkissä kirjastofunktion apufunktio toteutettiin yksityisenä funktiona. Scalan lohkorakennetta käyttäen apulaisen piilottaminen voidaan hoitaa myös sijoittamalla se tuon julkisen funktion sisään:
  import scala.io.Source
  object LongLines {
    def processFile(filename: String, width: Int) {

      def processLine(line: String) { // Huom: Lohkojen näkyvyyssääntöjen
        if (line.length > width)          //   takia tunnukset filename ja width
          println(filename +": "+ line)   //   näkyvät tänne eikä niitä tarvitse
      }                                   //   välittää parametreina.

      val source = Source.fromFile(filename)
      for (line <- source.getLines)
        processLine(line)
    }
  }

Funktiot ovat first-class-arvoja

Funktioliteraali on ohjelmatekstiin kirjoitettu nimetön funktio (vrt. kokonaislukuliteraali, merkkijonoliteraali, ...).

Esimerkiksi

   (x: Int) => x + 1
on kokonaislukuparametrinsa seuraajan palauttava nimetön funktio.

Ohjelmatekstiin kirjoitettua literaalia vastaa suoritusaikana jokin arvo. Esimerkiksi desimaalilukuliteraalia "3.14" vastaa double-liukuluvuksi tulkittava bittien jono, jne.

Funktioliteraalia puolestaan vastaa ohjelman suoritusaikana funktioarvo. Tällaisia voi sijoitella muuttujiin, välittää parametreina, käyttää funktioden paluuarvoina, jne... Tällä tavoin käytettävät arvot ovat ns. first-class-arvoja.

Useimmissa "tavallisissa" ohjelmointikielissä ainakin kokonais- ja liukuluvut ovat tällaisia. Javassa myös taulukko-oliot ovat first-class-arvoja, mutta useimmissa perinnekielissä taulukoita ei voi käyttää näin.

Scalassa (ja funktionaalisissa kielissä) "perinnekielistä" poiketen myös siis funktiot ovat first-class-arvoja. Tämä merkitsee siis mm. sitä, että funktio voidaan välittää parametrina ja että funktio voi palauttaa arvonaan funktion.

Funktioarvot on toteutettu oliona, funktio-oliona, joten niitä voi käyttää samaan tapaan kuin muitakin olioarvoja:

var increase = (x: Int) => x + 1
println(increase(10))  // 11

increase = (x: Int) => x + 100 // muuttujaa voi tietenkin muuttaa!
println(increase(10))  // 110

// 
Funktioarvot ovat oliota, joihin kääntäjä liittää piirretyyppejä, "mixaa traitteja", scalamaiseen tapaan:
Every function value is an instance of some class that extends one of several FunctionN traits in package scala, such as Function0 for functions with no parameters, Function1 for functions with one parameter, and so on. Each FunctionN trait has an apply method used to invoke the function.

Huom: Koska funktioarvo ja funktion arvo voivat helposti sekoittua keskenään, on turvallisempaa kutsua funktioarvoja nimellä funktio-olio!

Tuttuun tapaan funktiot ovat funktioita matematiikkaa lavaeammassa merkityksessä; sivuvaikutukset yms. ovat mahdollisia:

var apua = 0

val increase = (x: Int) => {
  apua = 13                 // ... sivuvaikutuksia ...
  println("Kuinkas")
  println("nyt")
  println("käy?")
  x + 666
}
println(increase(10))  // Kuinkas
                       // nyt
                       // käy?
                       // 676

println(apua)          // 13

Monet Scalan valmiit kirjastorutiinit on toteutettu siten, että parametrina annetaan funktio-olio – tyypillisesti muttei välttämättä funktioliteraalina. Myös nimettyjä funktioita voi välittää parametreina.

Esimerkiksi jo tutuksi tullut kokoelmaluokille määritelty foreach on tällainen. Ja esimerkiksi filter-metodilla voi suodattaa alkioita kokoelmaluokkien ilmentymistä totuusarvoisella funktioparametrilla, väittämällä, jonka totuusarvo määrää mukaan otettavat alkiot:

val someNumbers = List(-11, -10, -5, 0, 5, 10)
someNumbers.foreach((x: Int) => println(x))
println(someNumbers.filter((x: Int) => x > 0))  // List(5, 10)
Scalan päättelymekanismien ansiosta kirjoitusvaivoja voi usein vähentää:
println(someNumbers.filter((x) => x > 0))
println(someNumbers.filter(x => x > 0))

Paikanpitäjäparametrit

Paikanpitäjillä "_" (placeholder) voi jättää antamatta nimiä funktioliteraalien muodollisille parametreille:
val f = (_: Int) * (_: Int)
println(f(7, 8))                         // 56
              
val g = (_: String) + " ja " + (_: String)
println( g("kissa", "koira") )           // kissa ja koira  
Näin voi menetellä, jos funktion rungossa viitataan kuhunkin muodolliseen parametriin tasan kerran ja parametrien kirjoitusjärjestyksessä. Tyyppipäättelyn ansiosta tyyppininimiä voi toisinaan jättää kirjoittamatta.

Myös valmiin kaluston käyttö saadaan entistä näpsäkämmäksi(?):

val someNumbers = List(-11, -10, -5, 0, 5, 10)
println(someNumbers.filter(_ > 0))  // List(5, 10)
Funktioliteraali "_ > 0" sanoo siis saman kuin "x => x > 0".

Huom: Jokainen funktioliteraalissa esiintyvä alaviiva siis tarkoittaa eri parametria. Jos samaan parametriin halutaan viitata useampaan kertaan, tämä tapa ei toimi

Osittain sovelletut funktiot

Funktion kutsumista kutsutaan funktion soveltamiseksi (apply) argumentteihin eli todellisiin parametreihin. Esim. tässä
def sum(a: Int, b: Int, c: Int) = a + b + c
println(sum(1,2,3))   // 6
funktiota sum sovelletaan argumentteihin 1, 2 ja 3.

Tällaisesta "kokonaan sovellettavasta" funktiosta voidaan muodostaa uusia funktioita, osittain sovellettuja funktioita, kiinnittämällä osa parametreista:

def sum(a: Int, b: Int, c: Int) = a + b + c
val a = sum(2, _:Int, 4)
println(a(12))               // 18
val b = sum(1, _:Int, _:Int)
println(b(3,5))              // 9

val c = sum(_:Int, _:Int, _:Int)  // !!
println(c(1,2,3))                 // 6

val d = sum _       // myös koko parametrilistan voi korvata alaviivalla!
println(d(1,2,3))   // 6

Osittain sovellettu funktio (partially applied function) on siis sellainen funktio, jolle ei anneta kaikkia argumentteja – ehkä ei ensimmäistäkään.

Jos siis n:n argumentin funktion yksi argumentti kiinnitetään, saadaan (n-1):n argumentin funktio, jne.

Alaviiva "_" voi siis korvata myös kokonaisen muodollisten parametrien listan.

Tutkitaanpa näiden funktioiden tyyppejä:

scala> def sum(a: Int, b: Int, c: Int) = a + b + c
sum: (a: Int, b: Int, c: Int)Int

scala> val a = sum(2, _:Int, 4)
a: Int => Int = <function1>

scala> val b = sum(1, _:Int, _:Int)
b: (Int, Int) => Int = <function2>

scala> val c = sum(_:Int, _:Int, _:Int)
c: (Int, Int, Int) => Int = <function3>

scala> val d = sum _ 
d: (Int, Int, Int) => Int = <function3>

Huom: myös funktioille "sulkeiden semantiikka" on apply-metodin kutsu. Kutsun saa halutessaan myös kirjoittaa näkyviin:

def sum(a: Int, b: Int, c: Int) = a + b + c
val b = sum(1, _:Int, _:Int)

println(b(3,5))
println(b.apply(3,5))

Muuttuja b viittaa tässä funktioarvoon (eli funktio-olioon), joka on saanut Function2-piirretyypiltä apply-aksessorin.

Huom. Vaikka luokan metodeita tai jonkin funktion sisältämiä paikallisia funktioita ei omalla nimellään saa esim. välitettyä parametrina, funktioarvon sijoittaminen val- tai var-muuttujaan tekee tämän mahdolliseksi! ("wrappäys", "käärepaperointi").

Seuraavat tutut ja keskenään samaa tarkoittavat ilmauksetkin saivat nyt selityksensä:

  someNumbers.foreach(x => println(x)) // funktioliteraali tavalliseen tapaan
  someNumbers.foreach(println(_))      // funktioliteraali placeholder-parametrilla
  someNumbers.foreach(println _)       // placeholder koko parametrilistan sijaan
Tässä esimerkissä "osittain" siis tarkoittaa "ei lainkaan"... Nimetystä systeemifunktiosta println muodostetaan tällä tavoin funktioliteraali.

Kun vaikkapa foreach-operaatiolle halutaan antaa parametrina sellainen "osittain sovellettu" funktio, jota ei ole lainkaan sovellettu, esim. println _ tai sum _, tuon alaviivankin saa jättää pois, koska kääntäjä tietää, että foreach sallii ainoastaan funktion parametrina:

someNumbers.foreach(println _)
 // voidaan kirjoittaa vieläkin lyhyemmin:   ... ja taas kääntäjä päättelee...
someNumbers.foreach(println)
[En ole ihan vakuuttunut, että tämäkin lyhennysmerkintä oli viisasta ottaa kieleen!]

Sulkeuma

Sulkeuman perusidea on antaa todellisena parametrina jokin funktio muualla suoritettavaksi siten, että tuossa vieraassa ympäristössä käynnistettynäkin funktion koodin suoritus (eli viittaus vastaavaan muodolliseen parametriin) johtaa todellisen parametrin suorittamiseen todellisen parametrin omassa viittausympäristössä, siis kutsukohdan viittausympäristössä eli nimiavaruudessa.

Ja tarkkaan ottaen sulkeuma on siis suoritusaikainen otus! Sellainen voidaan ohjelmatekstissä määrätä syntymään (so. ohjelmoida) kirjoittamalla funktio tai funktioliteraali todelliseksi parametriksi.

Vaikka esimerkiksi funktioliteraalia (x: Int) => x > 0 voidaan kutsua sulkeumaksi, se ei kuitenkaan vielä oikeastaan "sulje" sisäänsä mitään. Sulkemisessa on kyse ns. vapaiden muuttujien kiinnittämisestä. Tuossa ilmauksessa x on ns. sidottu muuttuja, jonka merkitys on selvä: x on parametri, jolle annetaan aina arvo, kun ilmaus lasketaan. Muuttujan x merkitys rajoittuu tähän funktioliteraaliin.

Myös funktioliteraalin paikalliset tunnukset ovat sidottuja – niillä on aina sama merkitys:

val f = (x: Int) => {val kasvu = 2; x + kasvu}
println(f(6))  // 8

Mutta kun funktioliteraaliin kijoitetaan tunnus, joka ei tavalla tai toisella saa merkitystä itse literaali-ilmauksessa, kyseessä on ns. vapaa muuttuja. Tällaisen tunnuksen on luonnollisesti oltava määritelty ja näkyvissä siinä ohjelmakohdassa, johon literaali kirjoitetaan!

Huom: Sana "muuttuja" ilmauksissa "sidottu ja vapaa muuttuja" tarkoittaa muuttujaa matematiikan ja logiikan mielessä! Myös muut tunnukset kuin ohjelman muuttujien tunnukset (eli nimet) voivat olla sidottuja ja vapaita muuttujia – esim. vakioiden ja funktioiden tunnukset (eli nimet).

Kun kirjoitetaan (x: Int) => x + kasvu, funktioliteraalin ilmaus sisältää nyt muuttujan (matematiikan mielessä), joka ei saa merkitystä itse ilmauksesta. Ja tässä viimein on vapaa muuttuja! Ja nyt vastaa aletaan rakennella aitoja sulkeumia kiinnittämällä vapaat muuttujat (matematiikan mielessä), "sulkemalla" ne laskentarutiinin sisään!

Funktioliteraalia, jossa ei ole vapaita muuttujia, kutsutaan suljetuksi termiksi (closed term). Jos literaalissa on vapaita muuttujia, se on avoin termi (open term). Katsotaan, mitä tulkki sanoo tällaisista:

scala> (x: Int) => {val kasvu = 2; x + kasvu}  // suljettu termi
res0: (Int) => Int = <function1>

scala> (x: Int) => x + kasvu    // avoin termi, kasvu on vapaa
<console>:5: error: not found: value kasvu
       (x: Int) => x + kasvu
                       ^
Avoin termi ei tietenkään kelpaa kääntäjälle, koska tunnuksella kasvu ei ole mitään merkitystä tämän avoimen termin termin nimiavaruudessa. Ei esimerkiksi ole määritelty, että kasvu olisi kokonaislukumuuttuja.

Laaditaan sitten esimerkki tilanteesta, jossa sulkeumaan suljetaan vapaa muuttuja, jota päästään muokkaamaan vieraassa ympäristössä, ympäristössä, jonka nimiavaruuteen muutettava muuttuja ei kuulu:

def teeKolmesti  (f: => Unit) = { // kutsuu parametriaan kolmasti
  f;f;f                           // (ei tänne mitään x:ää näy!)
}

def sovellus {
  var x = 1
  def puuha() {println("töitä tehdään"); x+=1}
                                      // vapaa muuttuja x
  teeKolmesti(puuha)                  // suljetaan sulkeumaan!
  println(x)
}

sovellus  // töitä tehdään
          // töitä tehdään
          // töitä tehdään
          // 4

Joko alkoi selvitä? Tunnuksella x on merkitys vain funktiossa sovellus; tuo merkitys on Int-muuttuja. Se on puuha-funktiolle annettavan funktioliteraalin vapaa muuttuja, joka suljetaan teeKolmesti-funktiolle parametrina annettavaan funktio-olioon.

Tällainee funktio-olio on sulkeuma.

Käytännön esimerkki sulkeumaan suljetun muuttujan arvon muuttamisesta systeemifunktion vieraassa näkyvyysaluessa on vaikkapa seuraavanlainen foreach-funktion käyttö:

val lukuja = List(3, 9, 12)
var summa = 0
lukuja.foreach(summa += _)  // Jokaista listan alkiota kohden käydään
                            // kasvattamassa kutsuympäristön muuttujaa summa!
println(summa)              // 24
Muuttuja summa ei todellakaan näy List-kokoelman foreach-aksessorin määrittelykohtaan!

Scala on kovin joustava (liian?) ilmauksissaan: Esimerkkejä erilaisista vaihtoehtoisista tavoista antaa tämä sama sulkeuma foreach-metodin suoritettavaksi:

val lukuja = List(3, 9, 12)
var summa = 0

lukuja.foreach(summa += _)
lukuja.foreach((x) => summa += x)
lukuja.foreach({(x) => summa += x})
lukuja.foreach({(x:Int) => summa += x})

println(summa)  // 96

Sulkeumaan suljettu muuttuja säilyy vaikka alkuperäinen määrittely-ympäristö katoaisi

Edellä nähtiin esimerkkejä siitä, miten sulkeumaan suljettuja vapaita muuttujia päästiin käsittelemään viittausympäristöissä, jonne nuo muuttujat eivät muuten näkyisi.

Sulkeumilla on yksi ehkä vieläkin vahvempi ominaisuus: vaikka alkuperäinen viittausympäristö katoaisi – muuttujan määrittelyfunktion suoritus loppuisi – sulkeumaan suljettu muuttuja säilyy!

def teeLaskuri: (()=>Int) = {
  var lkm=0
  return {()=>lkm+=1; lkm}
}

val a = teeLaskuri
val b = teeLaskuri

println("a = " + a()) // a = 1
println("a = " + a()) // a = 2

b(); b(); b()

println("b = " + b()) // b = 4
println("a = " + a()) // a = 3
println("b = " + b()) // b = 5
println("b = " + b()) // b = 6
println("a = " + a()) // a = 4
println("b = " + b()) // b = 7
println("b = " + b()) // b = 8
Tässä parametriton funktio teeLaskuri palauttaa arvonaan funktio-olion, jonka sisään on suljettu paikallinen muuttuja lkm. Vaikka tuo paikallinen muuttuja paikallisten muuttujien tuttuun tapaan häviää, kun funktion teeLaskuri suoritus päättyy, sulkeumaan suljettu versio tuosta muuttujasta säilyy! Ja kuten esimerkistä näkyy, jokaisella sulkeumalla on vieläpä oma versionsa tuosta muuttujasta!

Huom: Tässä kutsu ilman tyhjiä sulkeita tuottaa yllätyksen:

println("a = " + a) // a = <function0>

Funktioparametrin evaluointiaika

"Evaluointi" on "laskentaa". Kun funktio välitetään parametrina, on kiinnostavaa, milloin parametrina annettava funktio suoritetaan, heti todellisia parametreja selvitettäessä vai vasta sitten, kun parametrin saanut funktio kutsuu muodollista parametria. Scalassa tämä valinta tehdään ilmauksella => muodollisen funktioparametrin määrittelyssä:

Lasketaan heti todellista parametria laskettaessa:

var arvo = "kiltti"

def fun(paraFun: Unit) {
  println("Kutsuttu funktio alkaa, arvo = " + arvo)
  println("Funktio kutsuu muodollisen parametrin funktiota")
  paraFun
  println("Kutsuttu funktio päättyy, arvo = " + arvo)
}

println("Kutsutaan funktiota")
fun( { println("Funktioliteraali asettaa muuttujan tuhmaksi")
       arvo = "tuhma"
     }
   )
Tulostus:
Kutsutaan funktiota
Funktioliteraali asettaa muuttujan tuhmaksi
Kutsuttu funktio alkaa, arvo = tuhma
Funktio kutsuu muodollisen parametrin funktiota
Kutsuttu funktio päättyy, arvo = tuhma

Lasketaan vasta, kun kutsuttu funktio kutsuu muodollista funktioparametriaan
var arvo = "kiltti"

def fun(paraFun: => Unit) {
  println("Kutsuttu funktio alkaa, arvo = " + arvo)
  println("Funktio kutsuu muodollisen parametrin funktiota")
  paraFun
  println("Kutsuttu funktio päättyy, arvo = " + arvo)
}

println("Kutsutaan funktiota")
fun( { println("Funktioliteraali asettaa muuttujan tuhmaksi")
       arvo = "tuhma"
     }
   )
Tulostus:
Kutsutaan funktiota
Kutsuttu funktio alkaa, arvo = kiltti
Funktio kutsuu muodollisen parametrin funktiota
Funktioliteraali asettaa muuttujan tuhmaksi
Kutsuttu funktio päättyy, arvo = tuhma

Asia ei muuksi muutu, jos käytetään nimettyä funktiota:
var arvo = "kiltti"
def ff =  { println("Funktioliteraali asettaa muuttujan tuhmaksi")
            arvo = "tuhma"
          }

println("Kutsutaan funktiota")
fun(ff)

Vielä pari esimerkkiä sulkeumista

Parametrina annettavan sulkeuman kaikki funktio-olioon suljetut tunnukset – myös arvon saavat muuttujat – lasketaan ja ymmärretään kutsukohdan eli sulkeuman määrittelyn viiteympäristössä, vaikka sulkeuman käynnistyskohdan nimiavaruus antaisi muita merkityksiä sulkeuman sisältämille vapaille muuttujille:
var a=100; var b=200

def teeJotakin(par: =>Unit){
  var a=1; var b=2
  par
  println(a+"/"+b)
}
def jokinRutiini {
  var a=10; var b=20
  teeJotakin(a=b)
  println(a+"/"+b)
}

jokinRutiini
teeJotakin(a=b)
println(a+"/"+b)

/* Tulostus:
   1/2
   20/20
   1/2
   200/200
*/
Toisena esimerkkinä omatekoinen toistorakenne:
def toista(algoritmi: =>Unit, kertaa:Int) {
  var x = 0; var y = 0;             // paikallisia, sattumalta samannimisiä
  for (i <- 1 to kertaa) algoritmi
}
var x = 0
val k = 2
toista(x+=k, 5)
println(x)                      // 10

var y = 7
val m = 9
toista( {y=m+y; println(y)}, 5) //  16
                                //  25
                                //  34
                                //  43
                                //  52
Kun toista-funktio kutsuu muodollista parametriaan algoritmi, käydään vastaavan todellisen parametrin koodi siis suorittamassa siinä viiteympäristössä, jossa tuo koodi parametriksi annettiin.

Luennolla kerrotaan (ja piirretään) mitä kaikki tämä tarkoittaa ohjelman suoritusaikaisessa mallissa, miten tämä on mahdollista. Minulle tuon mallin tunteminen on ollut ratkaisevaa tämä tyyppisten ohjelmarakenteiden ymmärtämiselle.


Toistuva parametri – vaihtelevan mittainen parametrilistan häntä

Scalaan on salakuljetettu myös Javan (>=1.5) varargs-tekniikka. Tyylikkäästi vai ei? No kyllä aika ...

Parametrilistan viimeinen ja vain viimeinen parametri voi olla vaihtelevanmittainen. Vastaavia todellisia parametreja voi olla nolla tai enemmän. Kaikkien tyyppi on sama. Esimerkki:

def tulosta(kaikki: String*) {
  for (yksiMonista <- kaikki)
    print(yksiMonista +"/")
  println
}

tulosta()
tulosta("kissa")
tulosta("kissa", "hiiri")
tulosta("kissa", "hiiri", "koira")
tulosta("kissa", "hiiri", "koira", "kani")

/* Tulostus:

   kissa/
   kissa/hiiri/
   kissa/hiiri/koira/
   kissa/hiiri/koira/kani/
*/
Funktion sisällä vaihtelevan mittaista parametrilistaa käsitellään taulukkona (vrt. Javan malli!). Todelliseksi parametriksi taulukko ei kelpaa. Mutta tokihan Scalasta löytyy ilmaus, jolla taulukon saa muutettua alkioidensa jonoksi:
val t = Array("apina", "ja", "gorilla")
tulosta(t: _*)                               // apina/ja/gorilla/
Huoh, ... mitäpä Scalasta ei löytyisi ... ;-)

Parametrien valinta ja oletusarvot

Funktion kutsussa on mahdollista antaa todelliset parametrit eri järjestyksessä kuin muodolliset parametrit on funktiossa määritelty. Tällöin kutsussa muodollisen parametrin nimeen sijoitetaan todellisen parametrin arvo:
def printOsamaara(osoittaja: Int, nimittaja: Int) {
  println(1.0 * osoittaja/nimittaja)
}

printOsamaara(3, 4)                      // 0.75
printOsamaara(nimittaja=3, osoittaja=4)  // 1.3333333333333333

printOsamaara(osoittaja=3, nimittaja=4)  // toki näinkin saa tehdä
printOsamaara(osoittaja=3, 4)            // ja näin
printOsamaara(3, nimittaja=4)            // ja näin

printOsamaara(4, osoittaja=3)            // mutta EI näin
error: parameter 'osoittaja' is already specified at parameter position 1

printOsamaara(nimittaja=4, 3)            // EIKÄ näin
                           ^
one error found
Muodollisten parametrien nimen kirjoittaminen funktion kutsuun saattaa myös parantaa ohjelman luettavuutta kunhan vain parametrit on nimetty viisaasti.

Funktion määrittelyssa parametreille voidaan parametrilistassa myöa antaa oletusarvoja:

def printOsamaara(osoittaja: Int = 3, nimittaja: Int = 4) {
  println(1.0 * osoittaja/nimittaja)
}

printOsamaara()              // 0.75
printOsamaara(2)             // 0.5
printOsamaara(osoittaja=2)   // 0.5
printOsamaara(nimittaja=5)   // 0.6

printOsamaara(nimittaja=5, osoittaja=20) // 4.0
printOsamaara(4, 5)                      // 0.8