2011-03-17 166 views

回答

47

正如你發佈的鏈接描述。你可以使用Y-combinator。這裏是例子:

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 

注意它不適用於大數字。 請注意尾部呼叫優化。

+0

哇,amaizing。謝謝。 – aioobe 2011-03-17 11:55:22

+9

「令人驚歎」的原始意義讓我覺得自己迷失在迷宮中。 'fix'是一個函數,它將一個函數作爲輸入,它本身只需一個參數,並返回另一個函數,它接受一個參數,然後它......好的,我需要從* somebody *中得到更好的解釋。 – Malvolio 2011-03-17 22:59:02

+0

但是這不允許尾部呼叫優化,我正確嗎? – fresskoma 2011-06-02 21:36:32

21

如果你不想打了「驚人的數學」你可以只恢復到斯卡拉的對象方面。

val fact = new Function1[Int,Int]{ 
    def apply(x:Int):Int = if(x==1) x else x * apply(x-1) 
} 
12

,以使它看起來更老派了,你也可以使用這個代碼風格:

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

這一個看起來比簡單''新功能[Int ,Int] {...}'。謝謝! – 2015-12-07 14:46:09

5

在這個線程添加到很多很好的迴應在這裏,事實上,Scala是沒有給我們尾部調用優化的不動點組合子一直困擾着我,以至於我決定寫一個宏來轉換成一個普通的,慣用的遞歸調用的Y組合子般的呼叫(帶尾調用優化的,當然)。我們的想法是,像

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

通話很容易翻譯成

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

我已經忍了宏實現目標斯卡拉2.11(含有少量的調整也應當與2.10工作)到this gist

有了這個宏,我們就可以不用擔心如堆棧溢出執行匿名方式普通遞歸任務

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

res0: BigInt = 33162750924506332411753933805763240382811... 
+0

非常棒。 – 2016-03-15 19:51:17

+0

我想我仍然想知道是否可以添加tailrec註釋,以便它可以進行尾部呼叫優化。 – 2016-03-15 19:54:10

+0

@JoshCason認爲斯卡拉足夠聰明,可以在我給出的例子中推斷tailrec。我不太確定更多涉及的用例是否誠實。 – 2016-10-19 23:44:59

1

一個非常簡單的方法:

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)