【问题标题】:Why do I get an infinite loop when using implicit conversions?为什么在使用隐式转换时会出现无限循环?
【发布时间】:2011-09-30 22:07:46
【问题描述】:

上下文

object Fibonacci {
  final val Threshold = 30

  def fibonacci(n: Int)(implicit implementation: Fibonacci): Int = implementation match {
    case f: functional.type if n > Threshold => fibonacci(n)(imperativeWithLoop) 
    case f: imperativeWithRecursion.type => f(n)
    case f: imperativeWithLoop.type => f(n)
    case f: functional.type => f(n)
  }

  sealed abstract class Fibonacci extends (Int => Int)

  object functional extends Fibonacci {
    def apply(n: Int): Int =
      if (n <= 1) n else apply(n - 1) + apply(n - 2)
  }

  object imperativeWithRecursion extends Fibonacci {
    def apply(n: Int) = {
      @scala.annotation.tailrec
      def recursion(i: Int, f1: Int, f2: Int): Int =
        if (i == n) f2 else recursion(i + 1, f2, f1 + f2)

      if (n <= 1) n else recursion(1, 0, 1)
    }
  }

  implicit object imperativeWithLoop extends Fibonacci {
    def apply(n: Int) = {
      def loop = {
        var res = 0
        var f1 = 0
        var f2 = 1
        for (i <- 2 to n) {
          res = f1 + f2
          f1 = f2
          f2 = res
        }
        res
      }

      if (n <= 1) n else loop
    }
  }
}

示例

object Main extends App { // or REPL
  import Fibonacci._
  println(fibonacci(6)(imperativeWithRecursion)) // 8
  println(fibonacci(6)(imperativeWithLoop)) // 8
  println(fibonacci(6)(functional)) // 8
  println(fibonacci(6)) // 8
  println(fibonacci(40)(functional)) // 102334155
}

说明 我在玩 Scala 并最终得到了这段代码。它可以编译并运行,但是...

问题:

1) 两者之间是否有任何区别(可读性、性能、已知错误等)

case f: functional.type => f(n)

case `functional` => functional(n)

这应该是更多的讨论,所以我不仅对事实感兴趣。欢迎任何意见。

2) 查看fibonacci 方法的第一行。这里是:

case f: functional.type if n > Threshold => fibonacci(n)(imperativeWithLoop)

如果我离开第二个参数列表(imperativeWithLoop),代码编译但在我运行时进入无限循环。有谁知道为什么?编译器知道默认实现imperativeWithLoop(不会产生错误)。那么为什么它不被隐式调用呢? (我认为它没有)

【问题讨论】:

    标签: scala recursion pattern-matching implicit implicit-conversion


    【解决方案1】:

    关于第一个问题,有一些细微的差别,但在这里都无关紧要。但是如果你把对象大写会更好,在这种情况下你可以这样写:

    case Functional => Functional(n)
    

    关于第二个问题,如果你省略了imperativeWithLoop,它将选择范围内最接近的隐式Fibonacci——implementation(已经发现它等于funcional)。因此它会使用与之前调用的完全相同的参数来调用自己,并因此进入一个无限循环。

    【讨论】:

    • 谢谢,我想我现在明白了。在这种情况下,编译器警告或其他东西会很好。
    猜你喜欢
    • 2017-01-31
    • 2016-07-31
    • 1970-01-01
    • 2018-11-06
    • 2016-08-01
    • 2017-05-17
    • 2014-07-13
    • 2010-10-23
    • 1970-01-01
    相关资源
    最近更新 更多