Anonimowa funkcja rekurencyjna w Scali

Czy istnieje sposób na napisanie funkcji anonimowej, która jest rekurencyjna w Scali? Myślę o czymś takim:

((t: Tree) => {
    print(t.value);
    for (c <- t.children)
        thisMethod(c)
})(root)

(powiązane pytanie: które języki obsługują* rekurencyjne * literały funkcji / funkcje anonimowe?)

Author: Community, 2011-03-17

5 answers

Jak opisano w linku, który zamieściłeś. Możesz użyć Y-combinator. Oto przykład:

scala> def fix[A,B](f: (A=>B)=>(A=>B)): A=>B = f(fix(f))(_)
fix: [A,B](f: ((A) => B) => (A) => B)(A) => B

scala> val fact = fix[Int,Int](f => a => if(a<=0) 1 else f(a-1) * a)
fact: (Int) => Int = <function1>

scala> fact(12)
res0: Int = 479001600

Zauważ, że nie działa z dużymi liczbami. Bądź ostrożny z optymalizacją połączeń ogonowych.

 46
Author: Rustem Suniev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-11 22:53:23

Jeśli nie chcesz trafić w "niesamowitą matematykę", możesz po prostu powrócić do aspektów obiektowych Scali.

val fact = new Function1[Int,Int]{
    def apply(x:Int):Int = if(x==1) x else x * apply(x-1)
}
 21
Author: Spangaer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-17 20:40:10

Aby wyglądać bardziej geekier można również użyć tego stylu kodu:

val fact = new ((Int) => Int){
  def apply(x:Int):Int = if(x==1) x else x * apply(x-1)
}
 12
Author: Peter Ertl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 16:05:24

Dodając do wielu dobrych odpowiedzi tutaj w tym wątku, fakt, że Scala nie daje nam Tail Call optimizable Fixed-point combinator, martwi mnie tak bardzo, że zdecydowałem się napisać makro, aby przetłumaczyć wywołanie podobne do Y-combinatora na zwykłe, idiomatyczne wywołanie rekurencyjne (z optymalizacją wywołań ogonowych, oczywiście). Chodzi o to, że połączenie takie jak

fix[Int,Int]((next) => (y) => ...body...)

Można łatwo przetłumaczyć na

({(input) =>
  object next {
    def apply(y:Int):Int = ...body...
  }
  next(input)
})

Wstawiłem implementację makr ukierunkowaną na Scalę 2.11 (z drobnymi poprawkami powinien również działać z 2.10) do tego gist.

Za pomocą tego makra możemy wykonywać zwykłe rekurencyjne zadania w sposób anonimowy bez obawy o przepełnienie stosu np.

import asia.blip.ymacro.YMacro._
(y[BigInt,BigInt]((xx) => (y) => if(y==1) 1 else y * xx(y-1)))(2000)

Daje

res0: BigInt = 33162750924506332411753933805763240382811...
 5
Author: In-Ho Yi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-03 10:49:25

Bardzo proste podejście:

val fact = { (x: Int) =>
  def f(x: Int): Int = if (x == 0) 1 else x * f(x-1)
  f(x)
}

// Use as anonymous function below
(1 to 5).map { (x: Int) =>
  def f(x: Int): Int = if (x == 0) 1 else x * f(x-1)
  f(x)
}

// res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 6, 24, 120)
 0
Author: pvillela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-13 11:58:28