web-dev-qa-db-ja.com

Scalaの収量とは何ですか?

RubyとPythonの収量を理解しています。 Scalaのyieldは何をしますか?

303
Geo

シーケンス内包表記 で使用されます(Pythonのリスト内包表記やジェネレーターなど、yieldも使用できます)。

forと組み合わせて適用され、結果のシーケンスに新しい要素を書き込みます。

単純な例(from scala-lang

/** Turn command line arguments to uppercase */
object Main {
  def main(args: Array[String]) {
    val res = for (a <- args) yield a.toUpperCase
    println("Arguments: " + res.toString)
  }
}

F#の対応する式は

[ for a in args -> a.toUpperCase ]

または

from a in args select a.toUpperCase 

linqで。

Rubyのyieldの効果は異なります。

201
Dario

受け入れられた答えは素晴らしいと思いますが、多くの人々がいくつかの基本的なポイントを把握できていないようです。

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

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

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

  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) => {...})
    

非常に単純なfor内包表記を見ると、map/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と呼ばれるメソッドを導入しました。その主な違いは、新しいフィルターされたコレクションを返す代わりに、オンデマンドでフィルターをかけることです。 filterメソッドには、コレクションの厳密さに基づいて定義された動作があります。これをよりよく理解するために、List(strict)およびStream(non-strict)を使用したいくつかのScala 2.7を見てみましょう。

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

filterListですぐに適用され、foundfalseであるため、オッズのリストを返すため、違いが生じます。その時のみforeachが実行されますが、この時点ではfoundが既に実行されているため、filterを変更しても意味がありません。

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

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を導入しました。これは、コレクションの厳格さに関係なく、always non-strictです。次の例は、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の動作を変更することなく、ほとんどの人が期待する結果が得られます。補足として、RangeはScala 2.7とScala 2.8の間で非厳密から厳密に変更されました。

805

はい、Earwickerが言ったように、これはLINQのselectとほぼ同等であり、RubyおよびPythonのyieldとはほとんど関係がありません。基本的に、C#のどこで書くか

from ... select ??? 

代わりにScalaにあります

for ... yield ???

for- comprehensionsはシーケンスで機能するだけでなく、LINQのように特定のメソッドを定義する型でも機能することを理解することも重要です。

  • タイプがmapのみを定義している場合、単一のジェネレーターで構成されるfor- expressionsを使用できます。
  • flatMapmapを定義する場合、複数のジェネレーターで構成されるfor- expressionを使用できます。
  • foreachを定義する場合、yieldなしのfor- loopsを許可します(単一および複数のジェネレーターの両方で)。
  • filterを定義する場合、for式でifで始まるfor- filter式を許可します。
23
Alexey Romanov

Scalaユーザー(これは私ではありません)からより良い答えが得られない限り、ここに私の理解があります。

forで始まる式の一部としてのみ表示され、既存のリストから新しいリストを生成する方法を示します。

何かのようなもの:

var doubled = for (n <- original) yield n * 2

したがって、入力ごとに1つの出力項目があります(重複を削除する方法があると思いますが)。

これは、他の言語のyieldによって可能になった「命令型の継続」とはまったく異なり、ほとんどすべての構造を持ついくつかの命令型コードから任意の長さのリストを生成する方法を提供します。

(C#に精通している場合は、yield returnよりも LINQ'sselect演算子に近い)。

13

Scalaのキーワードyieldは単純な構文糖であり、mapに簡単に置き換えることができます。 as Daniel Sobralはすでに詳細に説明しています

一方、yieldは、 と同様のジェネレーター(または継続)を探しているifに誤解を招く可能性がありますPython のもの。詳細については、このSOスレッドを参照してください: Scalaで 'yield'を実装するための好ましい方法は何ですか?

12
Richard Gomes

以下を考慮してください for-comprehension

val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i

次のように読み上げると役立つ場合があります

"For各整数iif3より大きい場合は、yield(プロデュース)iをリ​​ストAに追加します。 "

数学的な set-builder notation の観点では、上記のfor-comprehensionは

set-notation

として読むことができます

"For各整数 iifより大きい 3、それはセットのメンバー A。 "

または、代わりに

"A すべての整数のセットです i、そのようなそれぞれ i より大きい 3。 "

9
Mario Galic

歩留まりはforループに似ていますが、ループには表示できないバッファがあり、増分ごとに次のアイテムをバッファに追加し続けます。 forループの実行が完了すると、生成されたすべての値のコレクションが返されます。 Yieldは、単純な算術演算子として使用することも、配列と組み合わせて使用​​することもできます。理解を深めるための2つの簡単な例を次に示します

scala>for (i <- 1 to 5) yield i * 3

res:scala.collection.immutable.IndexedSeq [Int] = Vector(3、6、9、12、15)

scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)

scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)

scala> val res = for {
     |     n <- nums
     |     c <- letters
     | } yield (n, c)

res:Seq [(Int、Char)] = List((1、a)、(1、b)、(1、c)、(2、a)、(2、b)、(2、c)、( 3、a)、(3、b)、(3、c))

お役に立てれば!!

1
Manasa Chada
val aList = List( 1,2,3,4,5 )

val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)

println( res3 )
println( res4 )

これら2つのコードは同等です。

val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )

println( res3 ) 
println( res4 )

これらの2つのコードも同等です。

マップはyieldと同じくらい柔軟で、その逆も同様です。

0
dotnetN00b