動的スコープパターンを使用できます。もっと端的に言えば、これはスレッドローカル変数(scalaDynamicVariable
はそのためだけに行われます)を使用して現在のネストレベルを保存することを意味します。このパターンの特定の例については、この他の質問に対する私の回答を参照してください。引数として関数リテラル(暗黙のパラメーターを使用)を受け取る関数を定義するにはどうすればよいですか?
ただし、これは、非常に特定の方法のネストレベルを知りたい場合にのみ適しています。どのメソッドでも機能する一般的なメカニズムが必要な場合、これは機能しません(メソッドごとに個別の変数が必要になるため)。この場合、私が考えることができる唯一の選択肢はスタックを検査することですが、それはあまり信頼できないだけでなく、非常に遅いです。
更新:実際には、動的スコープパターンを一般的な方法で適用する方法があります(可能な方法であれば)。重要なのは、メソッドごとに一意のIDを暗黙的に取得できるようにすることです。そこから、このIDをキーとして使用してaDynamicVariable
をメソッドに関連付けるだけです。
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は関数インスタンスの単なるクラスです。これは現在のところ期待どおりに機能します(すべての単項関数リテラルは実装されている1つのクラスとして実装されているFunction1
ため、クラスは一意のIDとして機能します)が、実際には、将来のバージョンのscalaでは壊れてしまう可能性があります(可能性は低いですが)。 。したがって、自己責任で使用してください。
最後に、ニキータ・ボルコフのより機能的な提案が全体としてより良い解決策ではないかどうかを最初に真剣に評価することをお勧めします。