20

この質問のように:

早期終了でループするための関数型コード

コードが

def findFirst[T](objects: List[T]):T = {
  for (obj <- objects) {
    if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
  }
  None
}

Scalaでこのようなforループから単一の要素を生成するにはどうすればよいですか?

元の質問で提案されているように、findを使用したくありません。forループを使用して実装できるかどうか、またどのように実装できるかについて興味があります。

* アップデート *

まず、すべてのコメントに感謝しますが、私は質問で明確ではなかったと思います。私はこのようなものを狙っています:

val seven = for {
    x <- 1 to 10
    if x == 7
} return x

そして、それはコンパイルされません。2つのエラーは次のとおりです。-メソッド定義の外部に戻る-メソッドmainにreturnステートメントがあります。結果タイプが必要

この場合、find()の方が優れていることはわかっています。私は、言語を学習して探索しているだけです。また、複数のイテレータを使用するより複雑なケースでは、forを使用して検索することが実際に役立つと思います。

コメント投稿者に感謝します、私は質問の悪いポーズを補うために賞金を始めます:)

4

9 に答える 9

25

、などforの連鎖呼び出しよりも優れた構文を使用するループを使用する場合は、巧妙なトリックがあります。リストのような厳密なコレクションを繰り返す代わりに、イテレータやストリームのような怠惰なコレクションを繰り返します。厳密なコレクションから始める場合は、たとえば、で怠惰にします。.find.filter.toIterator

例を見てみましょう。

まず、「ノイズの多い」intを定義しましょう。これは、呼び出されたときに表示されます。

def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }

次に、これらのいくつかをリストに入力しましょう。

val l = List(1, 2, 3, 4).map(noisyInt)

偶数である最初の要素を探します。

val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v

上記の行は次のようになります。

Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)

...すべての要素がアクセスされたことを意味します。結果のリストにすべての偶数が含まれていることを考えると、これは理にかなっています。今回はイテレータを繰り返し処理してみましょう。

val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)

これにより、次のようになります。

Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator

ループは、結果が空のイテレータであるか空でないイテレータであるかを判断できる時点までしか実行されなかったことに注意してください。

最初の結果を取得するには、を呼び出すだけr2.nextです。

Optionタイプの結果が必要な場合は、次を使用します。

if(r2.hasNext) Some(r2.next) else None

編集このエンコーディングの2番目の例は次のとおりです。

val seven = (for {
    x <- (1 to 10).toIterator
    if x == 7
} yield x).next

...もちろん、を使用する場合は、少なくとも解決策が常にあることを確認する必要があります.next。または、すべてのsheadOptionに対して定義された、を使用して、を取得します。TraversableOption[Int]

于 2012-11-12T12:49:13.227 に答える
18

リストをストリームに変換して、forループに含まれるフィルターがオンデマンドでのみ評価されるようにすることができます。ただし、ストリームからの譲歩は常にストリームを返します。必要なのはオプションだと思います。したがって、最後のステップとして、結果のストリームに少なくとも1つの要素があるかどうかを確認し、そのヘッドをオプションとして返すことができます。headOption関数はまさにそれを行います。

def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
    (for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption
于 2012-11-16T15:19:30.597 に答える
3

return上でスケッチしたとおりに、つまりループの早い段階から実行してみませんか? Scala が内部で実際に行っていることに興味がある場合は、コードを で実行してください-print。Scala はループを a に脱糖foreachし、例外を使用して途中で終了しforeachます。

于 2012-11-12T12:32:48.583 に答える
2

つまり、条件が満たされた後にループを抜け出そうとしています。ここでの回答は、あなたが探しているものかもしれません。Scala でループから抜け出すにはどうすればよいですか? .

全体として、Scala での理解のために、マップ、フラットマップ、およびフィルター操作に変換されます。したがって、例外をスローしない限り、これらの関数から抜け出すことはできません。

于 2012-11-12T12:32:33.080 に答える
1

疑問に思っているのであれば、これがLineerSeqOptimized.scalaでのfindの実装方法です。どのリストが継承するか

override /*IterableLike*/
  def find(p: A => Boolean): Option[A] = {
    var these = this
    while (!these.isEmpty) {
      if (p(these.head)) return Some(these.head)
      these = these.tail
    }
    None
  }
于 2012-11-12T12:44:49.073 に答える
1

なぜ次のようなものではないのですか

object Main {     
  def main(args: Array[String]): Unit = {
    val seven = (for (
    x <- 1 to 10
    if x == 7
    ) yield x).headOption
  }
}

値が条件を満たす場合、変数sevenはオプション保持になりますSome(value)

于 2012-11-19T13:16:38.617 に答える
1

これは恐ろしいハックです。しかし、それはあなたが望む結果を得るでしょう。

慣用的には、ストリームまたはビューを使用して、必要な部分を計算するだけです。

def findFirst[T](objects: List[T]): T = {

def expensiveFunc(o : T)  = // unclear what should be returned here

case class MissusedException(val data: T) extends Exception

try {
  (for (obj <- objects) {
    if (expensiveFunc(obj) != null) throw new MissusedException(obj)
  })
  objects.head // T must be returned from loop, dummy
} catch {
  case MissusedException(obj) => obj
}

}

于 2012-11-12T12:54:54.370 に答える
0
objects iterator filter { obj => (expensiveFunc(obj) != null } next

トリックは、イテレータまたはストリーム、または objects.view のいずれかである、コレクションで遅延評価されたビューを取得することです。フィルターは、必要な範囲でのみ実行されます。

于 2012-11-15T16:30:44.357 に答える
0

お役に立てれば幸いです。

私は思う...「リターン」インプリメンテーションはありません。

object TakeWhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else
        seq(seq.takeWhile(_ == null).size)
}

object OptionLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T], index: Int = 0): T = if (seq.isEmpty) null.asInstanceOf[T] else
        Option(seq(index)) getOrElse func(seq, index + 1)
}

object WhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else {
        var i = 0
        def obj = seq(i)
        while (obj == null)
            i += 1
        obj
    }
}
于 2012-11-12T16:19:29.470 に答える