【问题标题】:Anonymous recursive function in ScalaScala中的匿名递归函数
【发布时间】:2011-07-17 06:51:14
【问题描述】:

有没有办法在 Scala 中编写一个递归的匿名函数?我正在考虑这样的事情:

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

(相关问题:Which languages support *recursive* function literals / anonymous functions?

【问题讨论】:

    标签: scala recursion anonymous-function


    【解决方案1】:

    如您发布的链接中所述。您可以使用 Y 组合器。这是一个例子:

    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
    

    请注意,它不适用于大数字。 注意尾调用优化。

    【讨论】:

    • “Amazing”的原意是让我感觉自己迷失在迷宫中。 fix 是一个函数,它接受一个函数作为输入,该函数本身接受一个参数并返回另一个接受一个参数的函数,然后它......好吧,我需要 somebody...
    • 但这不允许尾调用优化,对吗?
    • @Malvolio: fix 是一个函数,它接受另一个函数 f 作为其参数,并为该函数返回一个 固定点,即一个值 x其中f(x) == x。为其他函数计算不动点的函数称为fixed point combinators。 Y 组合器只是定点组合器的一个示例。定点组合器提供了一种方法来描述语言中的递归,例如不允许自引用定义的 lambda 演算。
    【解决方案2】:

    如果你不想达到“惊人的数学”,你可以回到 scala 的对象方面。

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

    【讨论】:

      【解决方案3】:

      为了让它看起来更怪异,你也可以使用这种代码风格:

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

      【讨论】:

      • 这个看起来比简单的new Function[Int, Int] {...}好多了。谢谢!
      【解决方案4】:

      除了这个线程中的许多好的响应之外,Scala 没有为我们提供尾调用可优化定点组合器这一事实一直困扰着我,以至于我决定编写一个宏来翻译 Y 组合器- 像调用一个普通的、惯用的递归调用(当然还有尾调用优化)。这个想法是这样的电话

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

      很容易翻译成

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

      我已经将针对 Scala 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...
      

      【讨论】:

      • 我想我仍然想知道您是否可以添加tailrec注释以便它可以进行尾调用优化。
      • @JoshCason 认为 Scala 足够聪明,可以在我给出的示例中推断 tailrec。老实说,我不太确定更多涉及的用例。
      【解决方案5】:

      Scala 上的递归调用。 让我以 N 个数的总和为例进行递归

      var sumIt:(Int => Int) = (x: Int) => {if(x<=1) 1 else sumIt(x-1)+x}
      
      
       scala> sumIt(10) 
      val res141: Int = 55
      

      您可以看到 sumIt 的类型为 Int,Int 为 Input 和返回值。 sumIt lambda 函数将 Integer 作为参数,并对 sumIt 进行递归调用。

      我只是为了方便理解递归调用这个例子。您可以直接使用此逻辑的公式...

      sumValue = (N*(N+1)) /2
      

      【讨论】:

        【解决方案6】:

        一个非常简单的方法:

        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)
        

        【讨论】:

        • 递归函数是f,不是匿名的。它在匿名函数内部定义的事实并没有改变这一点。
        • 投反对票是没有根据的,因为解决方案符合问题的预期目的。此外,您选择了这个解决方案,而上面还有两个与这个非常相似的解决方案,它们使用 def apply 而不是 def f。
        • def f 向上移动一行相比,您的解决方案是否更正确?因为如果不是,那说明了我的观点,如果是,请解释为什么除了 f 的范围之外它们不完全相同
        猜你喜欢
        • 1970-01-01
        • 2022-06-20
        • 2011-10-24
        • 2015-11-21
        • 2011-01-29
        • 2011-04-22
        • 2020-09-24
        • 2014-03-25
        • 2013-07-11
        相关资源
        最近更新 更多