RubyとPythonの収量を理解しています。 Scalaのyieldは何をしますか?
シーケンス内包表記 で使用されます(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
の効果は異なります。
受け入れられた答えは素晴らしいと思いますが、多くの人々がいくつかの基本的なポイントを把握できていないようです。
まず、Scalaのfor
内包表記はHaskellのdo
表記と同等であり、複数のモナド演算の合成のための構文糖にすぎません。この声明はおそらく助けを必要とする人には役に立たないので、もう一度試してみましょう…:-)
Scalaのfor
内包表記は、map、flatMap
およびfilter
を使用して複数の操作を構成するための構文糖衣です。またはforeach
。 Scalaは実際にfor
- expressionをこれらのメソッドの呼び出しに変換するため、それらを提供するクラスまたはそのサブセットを理解に使用できます。
まず、翻訳について話しましょう。非常に単純なルールがあります:
この
for(x <- c1; y <- c2; z <-c3) {...}
に翻訳されています
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
この
for(x <- c1; y <- c2; z <- c3) yield {...}
に翻訳されています
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
この
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
は使用できる場合は、前者へのフォールバックを使用します。詳細については、以下のセクションをご覧ください。
この
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
filter
はList
ですぐに適用され、found
はfalse
であるため、オッズのリストを返すため、違いが生じます。その時のみforeach
が実行されますが、この時点ではfound
が既に実行されているため、filter
を変更しても意味がありません。
Stream
の場合、条件はすぐには適用されません。代わりに、各要素がforeach
によって要求されると、filter
は条件をテストします。これにより、foreach
がfound
を通じて条件に影響を与えることができます。明確にするために、同等の理解コードを次に示します。
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の間で非厳密から厳密に変更されました。
はい、Earwickerが言ったように、これはLINQのselect
とほぼ同等であり、RubyおよびPythonのyield
とはほとんど関係がありません。基本的に、C#のどこで書くか
from ... select ???
代わりにScalaにあります
for ... yield ???
for
- comprehensionsはシーケンスで機能するだけでなく、LINQのように特定のメソッドを定義する型でも機能することを理解することも重要です。
map
のみを定義している場合、単一のジェネレーターで構成されるfor
- expressionsを使用できます。flatMap
とmap
を定義する場合、複数のジェネレーターで構成されるfor
- expressionを使用できます。foreach
を定義する場合、yieldなしのfor
- loopsを許可します(単一および複数のジェネレーターの両方で)。filter
を定義する場合、for
式でif
で始まるfor
- filter式を許可します。Scalaユーザー(これは私ではありません)からより良い答えが得られない限り、ここに私の理解があります。
for
で始まる式の一部としてのみ表示され、既存のリストから新しいリストを生成する方法を示します。
何かのようなもの:
var doubled = for (n <- original) yield n * 2
したがって、入力ごとに1つの出力項目があります(重複を削除する方法があると思いますが)。
これは、他の言語のyieldによって可能になった「命令型の継続」とはまったく異なり、ほとんどすべての構造を持ついくつかの命令型コードから任意の長さのリストを生成する方法を提供します。
(C#に精通している場合は、yield return
よりも LINQ'sselect
演算子に近い)。
Scalaのキーワードyield
は単純な構文糖であり、map
に簡単に置き換えることができます。 as Daniel Sobralはすでに詳細に説明しています 。
一方、yield
は、 と同様のジェネレーター(または継続)を探しているifに誤解を招く可能性がありますPython のもの。詳細については、このSOスレッドを参照してください: Scalaで 'yield'を実装するための好ましい方法は何ですか?
以下を考慮してください for-comprehension
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
次のように読み上げると役立つ場合があります
"For各整数i
、if3
より大きい場合は、yield(プロデュース)i
をリストA
に追加します。 "
数学的な set-builder notation の観点では、上記のfor-comprehensionは
として読むことができます
"For各整数 、ifより大きい 、それはセットのメンバー 。 "
または、代わりに
" すべての整数のセットです 、そのようなそれぞれ より大きい 。 "
歩留まりは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))
お役に立てれば!!
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と同じくらい柔軟で、その逆も同様です。