15

私は scala を学んでいますが、これは本当にクールな言語だと言わざるを得ません。私はそのパターン マッチング機能と関数リテラルが特に気に入っていますが、私は JavaScript と Ruby のバックグラウンドを持っており、これらの言語で私のお気に入りのパターンの 1 つは、遅延関数とメソッド定義パターンです。JavaScriptでの例は

var foo = function() {
  var t = new Date();
  foo = function() {
    return t;
  };
  return foo();
};

わずかな調整を加えた同じコードが ruby​​ で機能し、計算の実行後にシングルトン オブジェクトを使用してメソッドを再定義するだけです。この種のことは、高価な計算が含まれていて、結果が必要になるかどうかが事前にわからない場合に非常に便利です。scala では、キャッシュを使用して同じ種類の結果をシミュレートできることを知っていますが、条件付きチェックを回避しようとしており、これまでの実験では否定的な結果が返されました。scala に遅延関数またはメソッド定義パターンがあるかどうかは誰にもわかりませんか?

注: JavaScript コードは Peter Michaux のサイトからのものです。

4

6 に答える 6

32

JavaScript の複雑なコードはすべて、日付の値をキャッシュしようとしているように見えます。Scala では、同じことを簡単に実現できます。

lazy val foo = new Date

そして、val を作成したくないが、必要な場合にのみ高価なコードを実行する関数を呼び出したい場合は、次のことができます。

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 回使用すると、毎回再生成されることに注意してください。これは、Randall Schultz の便利なパターンの出番です。

def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
  lazy val e = expensive
  if (doIt) {
    println(e)
    println("Wow, that was " + e.length + " characters long!")
  }
}

これで、(by-name パラメーターを介して) 必要な場合にのみ生成し、それ保存して、(lazy val を介して) 再度必要になった場合に再利用します。

したがって、Scala を JavaScript によく似たものにすることはできますが、JavaScript の方法ではなく、この方法で行ってください。

于 2010-08-25T14:35:30.407 に答える
20

Scala にはlazy vals があり、その初期化子は val が使用されるまで評価されません。遅延値は、メソッドのローカル変数として使用できます。

Scala には、名前によるメソッド パラメーターもあり、その実際のパラメーター式はサンクでラップされ、そのサンクは、メソッド本体で仮パラメーターが参照されるたびに評価されます。

これらを一緒に使用して、Haskell のデフォルトのような遅延評価セマンティクスを実現できます (少なくとも、私の Haskell の非常に限られた理解では)。

def meth(i: => Int): Something = {
  //        ^^^^^^ by-name parameter syntax
  lazy val ii = i
  // Rest of method uses ii, not i
}

このメソッドでは、実パラメータとして使用される式は、ゼロ回 (メソッド本体の動的実行パスが を使用しないii場合) または 1 回 ( ii1 回以上使用する場合) 評価されます。

于 2010-08-25T03:25:08.713 に答える
10

関数である遅延 val を定義できます。

lazy val foo = {
  val d = new Date
  () => { d }
}

println(foo())

foo()毎回同じ Date オブジェクトを返すようになりました。このオブジェクトは、最初に foo が呼び出されたときに初期化されます。

コードを少し説明すると、最初に foo() が呼び出され{ val d = new Date; () => { d } }て実行され、d が新しい日付値に割り当てられ、最後の式が評価さ() => { d }れて foo 値に割り当てられます。次に、foo は d を返すパラメーターのない関数です。

于 2010-08-25T06:00:37.753 に答える
6

質問の言い方に少し戸惑った回答者もいたと思います。ここで必要なScala構造は、単純な怠惰な定義です。

lazy val foo = new java.util.Date

Dateオブジェクトの構築は、最大で1回行われ、fooへの最初の参照まで延期されます。

于 2010-08-25T09:39:08.230 に答える
4

Ruby については何も知りませんでしたが、scala にはシングルトン オブジェクト パターンもあります。

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> 

function を取得したい場合は、それを関数型のサブタイプにすることができます:

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
于 2010-08-25T09:22:13.283 に答える
2

「遅延関数」とは、関数リテラルまたは匿名関数のことだと思います。

Scalaでは、投稿したjavascriptコードと非常によく似た、このようなことができます。

val foo = () => {
    val t = new Date()
    val foo = () => {t}

    foo()
}

println ("Hello World:" + foo())

主な違いは次のとおりです。

  • 外側の foo を再割り当てできませんでした
  • 「関数」キーワードはありません。代わりに (s:String) => {code} のようなものを使用します
  • 最後のステートメントはブロックの戻り値なので、「return」を追加する必要はありません。
于 2010-08-25T05:51:41.437 に答える