代码之家  ›  专栏  ›  技术社区  ›  schrödingcöder

如何从递归函数中得到终止原因?

  •  5
  • schrödingcöder  · 技术社区  · 6 年前

    假设一个函数循环生成一个数值结果。如果达到最大迭代次数或满足“最优性”条件,则循环停止。在任何一种情况下,都会输出来自当前回路的值。得到这个结果和停止原因的函数方法是什么?

    https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf .

    object SquareRootAlg {
        def next(a: Double)(x: Double): Double = (x + a/x)/2
        def repeat[A](f: A=>A, a: A): Stream[A] = a #:: repeat(f, f(a))
    
        def loopConditional[A](stop: (A, A) => Boolean)(s: => Stream[A] ): A = s match {
              case a #:: t  if t.isEmpty => a
              case a #:: t => if (stop(a, t.head)) t.head else loopConditional(stop)(t)}  
      }
    

    import SquareRootAlg._
    val cond = (a: Double, b: Double) => (a-b).abs < 0.01
    val alg = loopConditional(cond) _
    val s = repeat(next(4.0), 4.0)
    
    alg(s.take(3))  // = 2.05, "maxIters exceeded"
    alg(s.take(5)) // = 2.00000009, "optimality reached"
    

    这个代码有效,但没有给出停止的原因。所以呢 我想写一个方法

     def loopConditionalInfo[A](stop: (A, A)=> Boolean)(s: => Stream[A]):  (A, Boolean) 
    

    (2.05, false) 在上述第一种情况下,以及 (2.00000009, true) 在第二个。有没有一种方法可以在不修改 next repeat

    2 回复  |  直到 6 年前
        1
  •  4
  •   Mike Allen    6 年前

    通常,您需要返回一个包含停止原因和结果的值。使用 (A, Boolean) 您提议的返回签名允许这样做。

    您的代码将变成:

    import scala.annotation.tailrec
    
    object SquareRootAlg {
      def next(a: Double)(x: Double): Double = (x + a/x)/2
      def repeat[A](f: A=>A, a: A): Stream[A] = a #:: repeat(f, f(a))
    
      @tailrec // Checks function is truly tail recursive.
      def loopConditional[A](stop: (A, A) => Boolean)(s: => Stream[A] ): (A, Boolean) = {
        val a = s.head
        val t = s.tail
        if(t.isEmpty) (a, false)
        else if(stop(a, t.head)) (t.head, true)
        else loopConditional(stop)(t)
      }
    }
    
        2
  •  1
  •   Andrey Tyukin    6 年前

    只需返回布尔值而不修改任何其他内容:

    object SquareRootAlg {
      def next(a: Double)(x: Double): Double = (x + a/x)/2
      def repeat[A](f: A => A, a: A): Stream[A] = a #:: repeat(f, f(a))
    
      def loopConditionalInfo[A]
        (stop: (A, A)=> Boolean)
        (s: => Stream[A])
      : (A, Boolean) = s match {
        case a #:: t if t.isEmpty => (a, false)
        case a #:: t => 
          if (stop(a, t.head)) (t.head, true) 
          else loopConditionalInfo(stop)(t)
      }
    }
    
    import SquareRootAlg._
    val cond = (a: Double, b: Double) => (a-b).abs < 0.01
    val alg = loopConditionalInfo(cond) _
    val s = repeat(next(4.0), 4.0)
    
    println(alg(s.take(3))) // = 2.05, "maxIters exceeded"
    println(alg(s.take(5)))
    

    (2.05,false)
    (2.0000000929222947,true)