2

次の関数を検討してください。

import java.util.concurrent.Callable;

def callable[T]( operation: =>T) : Callable[T] = {
  new Callable[T] {
    def call : T = operation
  }
}

REPL では、このコードは私が望むことを行います。

scala> val myCallable = callable {
     | println("Side effect!");
     | "Hi!"
     | }
myCallable: java.util.concurrent.Callable[String] = $anon$1@11ba4552

scala> myCallable.call
Side effect!
res3: String = Hi!

scala> myCallable.call
Side effect!
res4: String = Hi!

by-name パラメーターは、関数「call」が呼び出されるまで評価されず、その関数が呼び出されるたびに再評価されます。それが私が望む行動です。

しかし、仕様では、名前によるパラメーターについて次のように述べています。

「対応する引数は、関数適用の時点では評価されませんが、代わりに関数内で使用されるたびに評価されます。」

この説明から、私が望む動作に依存できるかどうかは不明です。「機能内での使用」とはどういう意味ですか? これが、Callable が定義された時点 (非常に「関数内」) ではなく、Callable が呼び出された時点 (無期限の将来) を参照していることをどのように知ることができますか?

コードは私がやりたいことをしています。しかし、この動作が信頼できるものであり、scala の将来のバージョンで「修正」される可能性のあるバグではないと確信できれば、安心できます。

4

4 に答える 4

2

「関数」は、パラメーターを渡す関数です。この一節が警告しようとしているのは、次のことです。

scala> def byName(arg: => String) = arg + arg
byName: (arg: => String)java.lang.String

scala> byName({println("hi") ; "foo" })
hi
hi
res0: java.lang.String = foofoo

つまり、引数を参照するたびに副作用が発生します。一度しか実行していないので、それはあなたのケースにはあまり関係ありません(呼び出しサイトではなく、関数内にある評価のポイントを除いて)。

于 2013-03-26T12:40:38.683 に答える
2

これはバグではありません。動作は意図したとおりです。「関数内で使用されるたびに評価される」ということは、再帰的に「関数内の式が評価されるときに、その式内で使用されるたびに評価される」と考えることができます。

于 2013-03-26T12:34:47.103 に答える
1

以前の回答を拡張し、これを回避する方法を明確にするために、一度だけ評価したい場合は、関数内の val で値をキャプチャできます。これを行うことで、「名前による」パラメーターの評価が行われ、同じ式の 2 つの評価が行われるのではなく、計算された値が複数回使用されます。

scala> def byName(arg: => String) = {val computedArg = arg; computedArg + computedArg}
byName: (arg: => String)java.lang.String

scala> byName({"println("hi") ; "foo" })
hi
res0: java.lang.String = foofoo

将来それを行う必要がある場合に備えて...

于 2013-03-26T12:47:20.980 に答える
0

最後に、メソッド パラメーターの真の遅延評価 (ゼロまたは 1 つの評価のみ) を取得するには、次のようにします。

def m1(i: => Int) = {
  lazy val li = i
  ...
  // any number of static or dynamic references to li
  ...
}
于 2013-03-26T13:58:12.783 に答える