3

我想对我的函数调用嵌套的深度有一些基本的了解。考虑以下:

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函数知道它的嵌套深度。想法?

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

4

3 回答 3

4

您可以使用动态范围模式。更简单地说,这意味着使用线程局部变量(scalaDynamicVariable就是为此而做的)来存储当前的嵌套级别。有关此模式的部分示例,请参阅我对其他问题的回答:如何定义将函数文字(带有隐式参数)作为参数的函数?

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

更新:实际上,有一种方法可以以通用方式应用动态范围模式(对于任何可能的方法)。重要的部分是能够为每个方法隐式地获取一个唯一的 id。从那里开始,只需使用此 id 作为将 a 关联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,我实际上获得了传递给的闭包的 id withFunctionNesting(您必须在需要检索当前嵌套的方法中调用它)。这就是我在依赖于实现方面犯错的地方:id只是函数实例的类。到目前为止,这确实可以按预期工作(因为每个一元函数文字都被实现为一个实现Function1的类,因此该类充当唯一的 id),但现实情况是它可能会在未来版本的 scala 中破坏(尽管不太可能) . 因此,使用它需要您自担风险。

最后,我建议您首先认真评估 Nikita Volkov 提出的更实用的建议是否不是一个更好的整体解决方案。

于 2012-11-07T20:01:49.683 回答
0

您可以从函数返回一个数字,并计算您在返回堆栈的过程中处于多少级。但是没有简单的方法可以像您给出的示例输出那样计数。

于 2012-11-07T19:58:46.473 回答
0

Since your question is tagged with "functional programming" following are functional solutions. Sure the program logic changes completely, but then your example code was imperative.

The basic principle of functional programming is that there is no state. What you're used to have as a shared state in imperative programming with all the headache involved (multithreading issues and etc.) - it is all achieved by passing immutable data as arguments in functional programming.

So, assuming the "state" data you wanted to pass was the current cycle number, here's how you'd implement a function using recursion:

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

Alternatively you could make your worker function non-recursive and "fold" it:

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

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

Both codes above will produce the following output:

I am decorated 3x
I am decorated 2x
I am decorated 1x
foo
于 2012-11-07T20:40:56.287 回答