Scalaの怠zy関数定義
-
01-10-2019 - |
質問
私はScalaを学んでいますが、それは本当にクールな言語だと言わざるを得ません。私は特にそのパターンマッチング機能と関数リテラルが好きですが、私はJavaScript、Rubyの背景から来ており、それらの言語で私のお気に入りのパターンの1つは怠zyな関数とメソッドの定義パターンです。 JavaScriptの例は次のとおりです
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
マイナーな調整を伴う同じコードがRubyで機能します。そこでは、Singletonオブジェクトを使用して、計算の実行後にメソッドを再定義します。この種のことは、高価な計算が関係している場合に本当に便利で、結果が必要になるかどうかは事前にわかりません。 Scalaでは、キャッシュを使用して同じ種類の結果をシミュレートできることを知っていますが、条件付きチェックを避けようとしているので、これまでのところ、私の実験は否定的な結果を返しています。 SCALAに怠zyな関数またはメソッド定義パターンがあるかどうかを知っている人はいますか?
注:JavaScriptコードはPeter Michauxのからです サイト.
解決
JavaScriptのその複雑なコードはすべて、日付の値をキャッシュしようとするだけです。 Scalaでは、同じことを簡単に達成できます。
lazy val foo = new Date
そして、ヴァルを作りたくないが、それが必要な場合にのみコードを実行する関数を呼び出したい場合は、あなたはできます
def maybeExpensive(doIt: Boolean, expensive: => String) {
if (doIt) println(expensive)
}
maybeExpensive(false, (0 to 1000000).toString) // (0 to 1000000).toString is never called!
maybeExpensive(true, (0 to 10).toString) // It is called and used this time
ここでパターン expensive: => String
「リクエストに応じて文字列を生成するものを教えてください」と考えることができるBy-nameパラメーターと呼ばれています。 2回使用すると、毎回再生することに注意してください。ランドールシュルツの便利なパターンが登場する場所です。
def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
lazy val e = expensive
if (doIt) {
println(e)
println("Wow, that was " + e.length + " characters long!")
}
}
これで、必要な場合にのみ生成します(by-nameパラメーターを介して) と それを保存して、必要に応じて再利用します(怠zyなバルを介して)。
あなたがそうであっても、JavaScriptの方法ではなく、このようにしてください たぶん......だろう ScalaにJavaScriptによく似ています。
他のヒント
Scalaは持っています lazy val
S、その初期化剤は、VALが使用されない限り、および使用されない限り評価されません。レイジーバルは、メソッドローカル変数として使用できます。
Scalaには、実際のパラメーター式がサンクに包まれており、そのサンクがメソッドボディで参照されるたびにサンクが評価される別名メソッドパラメーターもあります。
これらを一緒に使用して、Haskellのデフォルトのような怠zyな評価セマンティクスを実現することができます(少なくともHaskellの私の非常に限られた理解では)。
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
この方法では、実際のパラメーターとして使用される式がゼロ時間のいずれかを評価します(メソッドボディの動的実行パスが決して使用しない場合 ii
または一度(使用する場合 ii
1回以上)。
関数である怠zyなバルを定義できます。
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()
これで、毎回同じ日付オブジェクトを返します。これは、FOOが最初に呼び出されるときに初期化されるオブジェクトを返します。
コードを少し説明するために、初めてfoo()が呼び出されます { val d = new Date; () => { d } }
実行されると、Dは新しい日付値に割り当てられ、最後の式を評価します () => { d }
そして、それをfoo値に割り当てます。 Fooは、dを返すパラメーターがない関数です。
応答者の何人かは、あなたが質問を述べた方法で少し混乱していたと思います。ここで必要なScalaコンストラクトは、シンプルな怠zyな定義です。
lazy val foo = new java.util.Date
日付オブジェクトの構築はせいぜい1回発生し、FOOへの最初の参照まで延期されます。
Rubyについては何も知りませんでしたが、ScalaにはSingletonオブジェクトパターンもあります。
Welcome to Scala version 2.8.0.r22634-b20100728020027 (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object LazyInit {
| val msec = { println("Hi,I'm here!"); System.currentTimeMillis }
| }
defined module LazyInit
scala> System.currentTimeMillis
res0: Long = 1282728315918
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
Hi,I'm here!
1282728319929 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728322936 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728324490 : 1282728319930
scala>
関数を取得したい場合は、関数タイプのサブタイプにすることができます。
scala> object LazyFun extends (() => Long) {
| val msec = System.currentTimeMillis
| def apply() = msec
| }
defined module LazyFun
scala> System.currentTimeMillis
res2: Long = 1282729169918
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729190384 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729192972 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729195346 : 1282729190384
「怠zyな関数」の意味は、関数文字または匿名の関数だと思います。
Scalaでは、投稿したJavaScriptコードに非常によく似たこのようなことをすることができます。
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
主な違いは次のとおりです。
- 外側のfooを再割り当てすることはできませんでした
- 「関数」キーワードはありません。代わりに(s:string)=> {code}のようなものを使用します
- 最後のステートメントはブロックの返品値であるため、「return」を追加する必要はありません。