2014-02-13 2 views
4

Я изучаю Программирование Funcional в Scala, и довольно часто мне нужно отслеживать оценку функции, чтобы лучше понять, как она работает.Как проследить оценку функции в 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 

В настоящее время я делаю это вручную или вводя уродливую печать. Как я могу достичь этого в удобной элегантной форме?

ответ

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)) 
} 

Примечание: чтобы избавиться от переменной «акк» вы можете сделать второй аккумулятор в функции «идти»


Это один также возвращает й e, который вы просили, но не скрывает 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 
+0

Спасибо @goozez, он делает то, что я хочу, хотя мне интересно, есть ли более общее решение для применения к любой функции без написания такого утомительного кода. –

+0

@ Rafa: Я думал, как это сделать, не заслоняя f и foldRight. Может быть, это будет больше по душе. Я отредактировал свой ответ. Cheers – goozez

+0

Гораздо лучший ответ. Благодарю. –

Смежные вопросы