我想了解一下我的函数调用嵌套的深度。请考虑以下事项:
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指出了我正确的方向。
答案 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