【问题标题】:Writing a factorial tail recursive function in Scala在 Scala 中编写阶乘尾递归函数
【发布时间】:2018-06-08 03:32:29
【问题描述】:

我正在尝试用下面的方式写一个尾递归函数,但是编译器抛出了一个错误:

方法应用的参数过多:(v1: Int)Int in trait Function1 否则阶乘(x-1, x*acc)

我曾尝试用 Function2 替换 Function1 并给出 Function2[Int, Int, Int] = new Function2[Int, Int, Int]

但它仍然给我同样的错误。有人能指出我哪里出错了吗?

import scala.annotation.tailrec
var factorial: Function1[Int, Int] = new Function1[Int, Int] {
    @tailrec override def apply (x:Int, acc:Int=1): Int = {
        if (x<=1) acc
        else factorial(x-1, x*acc)
    }
}

factorial(5)

【问题讨论】:

    标签: scala function recursion functional-programming tail-recursion


    【解决方案1】:

    apply 内的 Function1 必须只接受一个参数,当你传递两个参数时。

    你可以改写如下:

    var factorial: Function1[Int, Int] = new Function1[Int, Int] {
      def apply (x:Int): Int = {
        @tailrec def loop(x: Int, acc: Int = 1): Int = {
          if (x<=1) acc
          else loop(x-1, x*acc)
        }
        loop(x)
      }
    }
    

    【讨论】:

    • 您可以阅读更多here 了解匿名函数不支持默认参数的原因。
    【解决方案2】:

    Function1代表一个函数,只有一个参数(第二个用于输出)

    因此,您需要使用单个参数定义您的 apply 方法,然后在其中使用嵌套函数进行递归:

      import scala.annotation.tailrec
      var factorial: Function1[Int, Int] = new Function1[Int, Int] {
    
        override def apply(x: Int): Int = {
          @tailrec
          def go (x: Int, acc: Int = 1) : Int = {
            if (x<=1) acc
            else go(x-1, x*acc)
          }
          go(x)
        }
      }
      factorial(5)
    

    【讨论】:

      【解决方案3】:

      您可以看到这个answer,它很好地解释了您的问题。您的问题是您试图将apply 定义为尾递归,但您没有在递归调用中调用自己,而是调用factorial

      首先,您应该使用Function2 作为您的类型,同样适用:

      import scala.annotation.tailrec
      
      import scala.annotation.tailrec
      var factorial: Function2[Int, Int, Int] = new Function2[Int, Int, Int] {
          @tailrec override def apply (x:Int, acc:Int=1): Int = {
            if (x<=1) acc
            else apply(x-1, x * acc)
          }
      }
      

      然后,如果您收到错误 could not optimize @tailrec annotated method apply: it contains a recursive call targeting a supertype,您应该递归调用 apply,因为函数是尾递归的,它总是应该作为最后一条语句完全调用它自己。

      scala> factorial(5, 1)
      res3: Int = 120
      

      【讨论】:

        【解决方案4】:

        Function2 接受 3 个类型参数。最后一个是输出类型。

        Scala REPL

        scala> :paste
        // Entering paste mode (ctrl-D to finish)
        
         val fac: Function2[Int, Int, Int] = new Function2[Int, Int, Int] {
            def apply(v1: Int, v2: Int): Int = if (v1 == 1) v2 else apply(v1 - 1, v1 * v2)
          }
        
        
        // Exiting paste mode, now interpreting.
        
        fac: (Int, Int) => Int = <function2>
        
        scala> fac(5, 1)
        res1: Int = 120
        

        您可以使用语法糖(scala 中的函数语法使用=&gt;)而不是使用接口/特征 Function2。

        scala> :paste
        // Entering paste mode (ctrl-D to finish)
        
        val fac: (Int, Int) => Int = (acc, c) => if (c == 1) acc else fac(acc * c, c - 1)
        
        
        // Exiting paste mode, now interpreting.
        
        fac: (Int, Int) => Int = $$Lambda$1092/1204822967@5c83ae01
        
        scala> fac(1, 5)
        res0: Int = 120
        

        【讨论】:

        • 查看 pamu 和 Yevhenii Popadiuk 的答案,我犯的错误是在调用函数时,因为我声明 acc=1,在调用函数时我只是使用 fact(5),这导致了问题。但是我不明白,即使声明了 acc=1 ,在调用它时,为什么还要传递值?
        • 在这种情况下你不能使用默认值语法,因为它是 trait 实例的创建。您可以在正常函数声明的情况下执行此操作
        【解决方案5】:

        或者,如果你喜欢一些语法糖,你可以写它:

          val f: (Int) => BigInt = (x) => {
            if (x <= 1) 1
            else x * f(x - 1)
          }
        
          println(f(30))
        

        或真正的尾递归函数:

          val f: (Int) => BigInt = (x) => {
        
            @tailrec
            def helper(x: Int, acc: BigInt = 1): BigInt = {
              if (x <= 1) acc
              else helper(x - 1, x * acc)
            }
        
            helper(x)
          }
        
          println(f(30))
        

        【讨论】:

          猜你喜欢
          • 2021-01-25
          • 2020-02-26
          • 2023-03-25
          • 2019-08-06
          • 1970-01-01
          • 2021-03-18
          • 2015-04-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多