如何在Scala中跟踪嵌套函数

时间:2012-11-07 19:50:50

标签: scala functional-programming

我想了解一下我的函数调用嵌套的深度。请考虑以下事项:

scala> def decorate(f: => Unit) : Unit = { println("I am decorated") ; f }
decorate: (f: => Unit)Unit

scala>  decorate { println("foo") }
I am decorated
foo

scala> decorate { decorate { println("foo") } }
I am decorated
I am decorated
foo

对于最后一次通话,我希望能够得到以下内容:

I am decorated 2x
I am decorated 1x
foo

这个想法是decorate函数知道它的嵌套有多深。想法?

更新:正如Nikita所想,我的例子并不代表我真正追求的目标。目标不是生成字符串,以便能够通过对同一嵌套函数的一系列调用来传递某些状态。我认为RégisJean-Gilles指出了我正确的方向。

3 个答案:

答案 0 :(得分:4)

您可以使用动态范围模式。更具说服力的是,这意味着使用线程局部变量(scala的DynamicVariable就是这样做)来存储当前的嵌套级别。有关此模式的一个具体示例,请参阅我对此其他问题的回答:How to define a function that takes a function literal (with an implicit parameter) as an argument?

仅当您想要了解非常特定方法的嵌套级别时才适用。如果你想要一个适用于任何方法的通用机制,那么这将不起作用(因为你需要为每个方法使用一个不同的变量)。在这种情况下,我能想到的唯一选择是检查堆栈,但不仅非常可靠,而且速度也非常慢。

UPDATE :实际上, 是一种以通用方式应用动态范围模式的方法(对于任何可能的方法)。重要的是能够隐式地为每个方法获取唯一的id。从那里开始,只需使用此id作为将DynamicVariable与方法相关联的键:

import scala.util.DynamicVariable
object FunctionNestingHelper {  
  private type FunctionId = Class[_]
  private def getFunctionId( f: Function1[_,_] ): FunctionId = {
    f.getClass // That's it! Beware, implementation dependant.
  }
  private val currentNestings = new DynamicVariable( Map.empty[FunctionId, Int] )
  def withFunctionNesting[T]( body: Int => T ): T = {
    val id = getFunctionId( body )
    val oldNestings = currentNestings.value 
    val oldNesting = oldNestings.getOrElse( id, 0 )
    val newNesting = oldNesting + 1
    currentNestings.withValue( oldNestings + ( id -> newNesting) ) {
      body( newNesting )
    }    
  }
}

用法:

import FunctionNestingHelper._
def decorate(f: => Unit)  = withFunctionNesting { nesting: Int =>
  println("I am decorated " + nesting + "x") ; f 
}

要获取该方法的唯一ID,我实际上获得了传递给withFunctionNesting的闭包的id(您必须在需要检索当前嵌套的方法中调用)。这就是我在实现依赖方面犯错的地方: id 只是函数实例的类。这确实如预期的那样工作(因为每个一元函数文字实现为一个实现Function1的类,因此该类充当唯一的id),但实际情况是它可能会破坏(尽管不太可能) scala的未来版本。因此,使用它需要您自担风险。

最后,我建议你首先要认真评估尼基塔·沃尔科夫的更多功能性建议是不是一个更好的解决方案。

答案 1 :(得分:0)

您可以从函数中返回一个数字,并计算在备份堆栈的过程中您所处的级别。但是没有简单的方法可以像你给出的示例输出一样指望下行。

答案 2 :(得分:0)

由于您的问题标有“功能编程”,因此以下是功能解决方案。当然程序逻辑完全改变了,但是你的示例代码是必不可少的。

函数式编程的基本原理是没有状态。您曾经在命令式编程中作为共享状态使用了所有涉及的问题(多线程问题等) - 它都是通过在函数式编程中传递不可变数据作为参数来实现的。

因此,假设您想要传递的“状态”数据是当前的循环编号,以下是使用递归实现函数的方法:

def decorated ( a : String, cycle : Int ) : String
  = if( cycle <= 0 ) a
    else "I am decorated " + cycle + "x\n" + decorated(a, cycle - 1)

println(decorated("foo", 3))

或者你可以使你的工作者功能非递归并“折叠”它:

def decorated ( a : String, times : Int )
  = "I am decorated " + times + "x\n" + a

println( (1 to 3).foldLeft("foo")(decorated) )

以上两个代码都会产生以下输出:

I am decorated 3x
I am decorated 2x
I am decorated 1x
foo