k47.cz
výběr kolo foto makro povídky kultura
koronavirus TECH ▞▞ | twitter RSS

Scala - klasický for cyklus

3. 1. 2011 (aktualizováno 30. 5. 2021) — k47 (CC by)

Scala, na rozdíl od jazyků z rodiny C/C++, ne­na­bízí kla­sický for cyklus. Podle Mar­tina Ode­r­skyho je příliš im­pe­ra­tivní a Scala jako taková tíhne k funk­ci­o­nál­nímu stylu.

Nicméně, to nám ne­brání si tuto ja­zy­ko­vou kon­strukci do­pl­nit. Tady jsou moje tři po­kusné im­ple­men­tace.

// negenerická verze
def for0(init: Int, cond: Int => Boolean, incr: Int => Int)(op: Int => Unit) {
  var i = init; while (cond(i)) {
    op(i)
    i = incr(i)
  }
}

for0(0, _ < 10, _ + 1) { i => println(i) }
// generická verze 1
def for1[@specialized T](init: T, cond: T => Boolean, incr: T => T)(op: T => Unit) {
  var i = init; while (cond(i)) {
    op(i)
    i = incr(i)
  }
}

// typová anotace [Int] je nutná, bez ní kompilátor neodvodí typ fukncí cond a incr
for1[Int](0, _ < 10, _ + 1) { i => println(i) }
// generická verze 2
def for2[@specialized T](init: T)(cond: T => Boolean, incr: T => T)(op: T => Unit) {
  var i = init; while (cond(i)) {
    op(i)
    i = incr(i)
  }
}

// žádná typová anotace netřeba za cenu jiného zápisu
for2(0)(_ < 10, _ + 1) { i => println(i) }

Vý­sle­dek bo­hu­žel není tak fle­xi­bilní jako for cyklus v ja­zy­cích, které ho mají v sobě přímo ve­sta­věný.


Scala 3 do hry vnáší klí­čové slovo inline a to zcela mění si­tu­aci. Kód vypadá, chová se a po­u­žívá se stejně, jen se liší pod ka­po­tou.

inline def for3[T](inline init: T)(inline cond: T => Boolean, inline incr: T => T)(inline op: T => Unit) = {
  var i = init
  while (cond(i)) {
    op(i)
    i = incr(i)
  }
}

Klí­čové slovo inline zna­mená, že každé po­u­žití funkce for3 bude plně in­li­no­váno scala kom­pi­lá­to­rem. Když pak napíšu ná­sle­du­jící funkci sčí­ta­jící prvky pole, by­te­kód vypadá iden­ticky, jako kdy­bych while smyčku napsal ručně. Není v něm žádná abs­trakce, žádní in­di­rekce, žádné volání ano­nym­ních funkcí, žádná alo­kace pro­měnné s, ke níž se při­stu­puje z clo­sure, na heapu.

def sum(xs: Array[Double]) = {
  var s = 0.0
  for3(0)(_ < xs.length, _ + 1){ i => s += xs(i) }
  s
}

JVM JIT by udělal po­dobné op­ti­ma­li­zace, in­li­no­val by vše, co se dá a eli­mi­no­vat abs­trakce, ale nemusí se mu to vždy povést. Někdy heu­ris­tiky selžou, třeba při příliš hlu­bo­kém za­no­ření. S inline máme ga­ranci, že se abs­trakce zba­víme a kód ke prostá jed­no­du­chá a snadno op­ti­ma­li­zo­va­telná smyčka.

píše k47 & hosté, ascii@k47.cz