【问题标题】:How to trace a function evaluation in Scala?如何在 Scala 中跟踪函数评估?
【发布时间】:2014-02-13 22:22:02
【问题描述】:

我正在学习 Scala 中的函数式编程,并且经常需要跟踪函数评估以更好地了解它的工作原理。

例如,具有以下功能:

def foldRight[A,B](l: List[A], z: B)(f: (A, B) => B): B =
  l match {
    case Nil => z
    case Cons(x, xs) => f(x, foldRight(xs, z)(f))
  }

对于以下调用:

foldRight(Cons(1, Cons(2, Cons(3, Nil))), 0)(_ + _)

我想打印它的评估轨迹,像这样:

foldRight(Cons(1, Cons(2, Cons(3, Nil))), 0)(_ + _)
1 + foldRight(Cons(2, Cons(3, Nil)), 0)(_ + _)
1 + (2 + foldRight(Cons(3, Nil), 0)(_ + _))
1 + (2 + (3 + (foldRight(Nil, 0)(_ + _))))
1 + (2 + (3 + (0)))
6

目前我正在手动或注入丑陋的打印件。我怎样才能以一种方便优雅的方式实现这一目标?

【问题讨论】:

    标签: scala functional-programming trace


    【解决方案1】:

    我假设 Cons 和 :: 是相同的操作。 如果您不介意只获取当前元素和累加器,您可以执行以下操作:

    def printable(x:Int, y:Int): Int = {
        println("Curr: "+x.toString+" Acc:"+ y.toString)
        x+y
    } 
    foldRight(List(1, 2, 3, 4), 0)(printable(_,_))  
    //> Curr: 4 Acc:0
    //| Curr: 3 Acc:4
    //| Curr: 2 Acc:7
    //| Curr: 1 Acc:9
    //| res0: Int = 10
    

    如果您想要整个“堆栈跟踪”,这将为您提供您所要求的输出,尽管它远非优雅:

    def foldRight[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
      var acc = if (l.isEmpty) "" else l.head.toString
      def newAcc(acc: String, x: A) = acc + " + (" + x
      def rightSide(xs: List[A], z: B, size: Int) = xs.toString + "," + z + ")" * (l.size - size + 1)
      def printDebug(left: String, right: String) = println(left + " + foldRight(" + right)
    
      def go(la: List[A], z: B)(f: (A, B) => B): B = la match {
        case Nil => z
        case x :: xs => {
          acc = newAcc(acc, x)
          printDebug(acc, rightSide(xs, z, la.size))
          f(x, go(xs, z)(f))
        }
      }
      if (l.isEmpty) z
      else f(l.head, go(l.tail, z)(f))
    }
    

    注意:要摆脱变量“acc”,您可以在“go”函数中创建第二个累加器


    这个也返回你要求的输出,但不会掩盖 foldRight。

    class Trace[A](z: A) {
      var list = List[A]()
      def store(x: A) = {
        list = list :+ x
      }
    
      def getTrace(level: Int): String = {
        val left = list.take(level).map(x => s"$x + (").mkString
        val right = list.drop(level).map(x => s"$x,").mkString
        if (right.isEmpty)
          s"${left.dropRight(4)}" + ")" * (list.size - 1)
        else
          s"${left}foldRight(List(${right.init}), $z)" + ")" * (list.size - level - 1)
      }
    
      def getFullTrace: String =
        { for (i <- 0 to list.size) yield getTrace(i) }.mkString("\n")
    
      def foldRight(l: List[A], z: A)(f: (A, A) => A): A = l match {
        case Nil => z
        case x :: xs => store(x); f(x, foldRight(xs, z)(f))
      }
    }
    
    val start = 0
    val t = new Trace[Int](start)
    t.foldRight(List(1, 2, 3, 4), start)(_ + _) 
    t.getFullTrace 
    

    【讨论】:

    • 感谢@goozez,它可以满足我的需求,尽管我想知道是否有更通用的解决方案可以应用于任何函数,而无需编写如此繁琐的代码。
    • @Rafa:我一直在考虑如何在不遮挡 f 和 foldRight 的情况下做到这一点。也许这个会更符合你的喜好。我已经编辑了我的答案。干杯
    猜你喜欢
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多