Scalaの利回りとは何ですか?質問する

Scalaの利回りとは何ですか?質問する

Ruby と Python の yield は理解できました。Scala の yield は何をしますか?

ベストアンサー1

受け入れられた答えは素晴らしいと思いますが、多くの人がいくつかの基本的な点を理解していないようです。

まず、Scala のfor内包表記は Haskell の記法と同等でありdo、複数のモナド演算を合成するための構文糖に過ぎません。この説明はおそらく助けを必要としている人の役には立たないので、もう一度試してみましょう... :-)

Scala のfor内包表記は、map や を使用した複数の操作の合成に対する構文糖衣です。またはflatMap。Scalaは実際に-式をそれらのメソッドの呼び出しに変換するため、それらを提供するクラス、またはそのサブセットは for 内包表記で使用できます。filterforeachfor

まず、翻訳についてお話ししましょう。非常に簡単なルールがあります。

  1. これ

    for(x <- c1; y <- c2; z <-c3) {...}
    

    翻訳すると

    c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
    
  2. これ

    for(x <- c1; y <- c2; z <- c3) yield {...}
    

    翻訳すると

    c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
    
  3. これ

    for(x <- c; if cond) yield {...}
    

    Scala 2.7では次のように翻訳されます

    c.filter(x => cond).map(x => {...})
    

    または、Scala 2.8では、

    c.withFilter(x => cond).map(x => {...})
    

    withFilterメソッドが利用できないが、利用できる場合は前者にフォールバックしますfilter。詳細については、以下のセクションを参照してください。

  4. これ

    for(x <- c; y = ...) yield {...}
    

    翻訳すると

    c.map(x => (x, ...)).map((x,y) => {...})
    

非常に単純な内包表記を見るとformap/ のforeach代替案は確かに優れているように見えます。しかし、いったんそれらを作成し始めると、括弧とネスト レベルで簡単に迷子になる可能性があります。そうなると、for内包表記の方が通常ははるかに明確になります。

ここでは簡単な例を 1 つ示し、あえて説明は省略します。どちらの構文が理解しやすいかは、ご自身で判断してください。

l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))

または

for {
  sl <- l
  el <- sl
  if el > 0
} yield el.toString.length

withFilter

Scala 2.8 では というメソッドが導入されましたwithFilter。このメソッドの主な違いは、新しいフィルタリングされたコレクションを返すのではなく、オンデマンドでフィルタリングすることです。このメソッドの動作は、コレクションの厳密さに基づいて定義されます。これをよりよく理解するために、 (strict) と(non-strict)filterを使用した Scala 2.7 を見てみましょう。ListStream

scala> var found = false
found: Boolean = false

scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9

scala> found = false
found: Boolean = false

scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3

違いは、filterが に直ちに適用されList、オッズのリストを返すために発生します。 は でfoundあるためですfalse。 その後にのみforeachが実行されますが、この時点では、 が既に実行されているため、変更はfound無意味ですfilter

の場合Stream、条件はすぐには適用されません。代わりに、各要素が によって要求されると、 はforeach条件filterをテストし、foreachを通じてそれに影響を与えることができますfound。明確にするために、同等の for 内包コードを以下に示します。

for (x <- List.range(1, 10); if x % 2 == 1 && !found) 
  if (x == 5) found = true else println(x)

for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) 
  if (x == 5) found = true else println(x)

これは多くの問題を引き起こしました。なぜなら、人々はif、が事前にコレクション全体に適用されるのではなく、オンデマンドで考慮されると予想していたからです。

Scala 2.8では、コレクションの厳密性に関係なく、常にwithFilter非厳密となる が導入されました。次の例は、Scala 2.8 で両方の方法を示しています。List

scala> var found = false
found: Boolean = false

scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9

scala> found = false
found: Boolean = false

scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3

これにより、動作を変更することなく、ほとんどの人が期待する結果が得られますfilter。補足として、RangeScala 2.7 と Scala 2.8 の間では、非厳密から厳密に変更されました。

おすすめ記事