2011-03-17 144 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)