9

私はScalaでいくつかの実験をしようとしています。期待する結果が出るまでこの実験(ランダム化)を繰り返し、その結果を得たいと思います。whileループまたはdo-whileループのいずれかを使用してこれを行う場合は、次のように記述する必要があります(「body」は実験を表し、「cond」はそれが期待されるかどうかを示します)。

do {
  val result = body
} while(!cond(result))

ただし、最後の条件はループ本体からローカル変数を参照できないため、機能しません。このコントロールの抽象化を次のように少し変更する必要があります。

def repeat[A](body: => A)(cond: A => Boolean): A = {
  val result = body
  if (cond(result)) result else repeat(body)(cond)
}

どういうわけか動作しますが、次の2つのパラメーターを渡してこのメ​​ソッドを呼び出す必要があるため、完全ではありません。

val result = repeat(body)(a => ...)

組み込みの構造のように見えるように、これを行うためのより効率的で自然な方法があるかどうか疑問に思っています。

val result = do { body } until (a => ...)

戻り値のないbodyの優れたソリューションの1つは、この投稿にあります。RepeatUntilでScalaコントロールの抽象化を行うにはどうすればよいですか?、最後のワンライナーの答え。そのbody答えのその部分は値を返さないので、untilは新しいオブジェクトのメソッドである可能性がありますが、ではなくAnyRef戻りたいので、そのトリックはここでは適用されません。これを達成する方法はありますか?ありがとう。AAnyRef

4

3 に答える 3

6

あなたはプログラミングスタイルを混ぜ合わせていて、それが原因で問題を抱えています。

ループ内で何らかの副作用が発生しない限り、ループはプロセッサを加熱する場合にのみ有効です。

do {
  val result = bodyThatPrintsOrSomething
} until (!cond(result))

したがって、副作用のあるコードを使用する場合は、条件を変数に入れるだけです。

var result: Whatever = _
do {
  result = bodyThatPrintsOrSomething
} until (!cond(result))

または同等のもの:

var result = bodyThatPrintsOrSomething
while (!cond(result)) result = bodyThatPrintsOrSomething

あるいは、機能的なアプローチを取る場合は、とにかく計算の結果を返す必要があります。次に、次のようなものを使用します。

Iterator.continually{ bodyThatGivesAResult }.takeWhile(cond)

Iterator(すべての良いものとリストの最初の悪いものをとることで素晴らしい仕事をしないことの既知の迷惑があります)。

repeatまたは、末尾再帰であるメソッドを使用できます。信頼できない場合は、バイトコード(を使用javap -c)を確認し、注釈を追加して、@annotation.tailrec末尾再帰でない場合にコンパイラがエラーをスローするようにするか、次のvarメソッドを使用してwhileループとして記述します。

def repeat[A](body: => A)(cond: A => Boolean): A = {
  var a = body
  while (cond(a)) { a = body }
  a
}
于 2012-11-25T20:31:55.533 に答える
5

マイナーな変更を加えるだけで、現在のアプローチを一種のミニ流暢なAPIに変えることができます。これにより、必要なものに近い構文が得られます。

class run[A](body: => A) {
  def until(cond: A => Boolean): A = {
    val result = body
    if (cond(result)) result else until(cond)
  }
}
object run {
  def apply[A](body: => A) = new run(body)
}

doは予約語なので、と一緒に行かなければなりませんrun。結果は次のようになります。

run {
  // body with a result type A
} until (a => ...)

編集:

リンクされた質問ですでに提案されているものをほとんど再発明したことに気づきました。そのアプローチを拡張して、A代わりに型を返す1つの可能性は次のUnitとおりです。

def repeat[A](body: => A) = new {
  def until(condition: A => Boolean): A = {
    var a = body
    while (!condition(a)) { a = body }
    a     
  }   
}
于 2012-11-26T09:32:58.223 に答える
0

以前に行われた提案の派生物を文書化するためにrepeat { ... } until(...)、反復回数の制限も含まれる末尾再帰の実装を使用しました。

def repeat[A](body: => A) = new {
  def until(condition: A => Boolean, attempts: Int = 10): Option[A] = {
    if (attempts <= 0) None
    else {
      val a = body
      if (condition(a)) Some(a)
      else until(condition, attempts - 1)
    }
  }
}

attemptsこれにより、本体の実行後にループをベイルアウトできます。

scala> import java.util.Random
import java.util.Random

scala> val r = new Random()
r: java.util.Random = java.util.Random@cb51256

scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res0: Option[Int] = Some(98)

scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res1: Option[Int] = Some(98)

scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res2: Option[Int] = None

scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res3: Option[Int] = None

scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res4: Option[Int] = Some(94)
于 2016-04-12T21:44:26.903 に答える