回答:
これは、シーケンス内包表記(Pythonのリスト内包表記やジェネレーターなどyield
)で使用されます。
組み合わせて適用さfor
れ、結果のシーケンスに新しい要素を書き込みます。
簡単な例(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
内包do
表記はHaskellの表記法と同等であり、複数のモナド演算を合成するための構文糖にすぎません。このステートメントはおそらく助けを必要とする人には役に立たないので、もう一度試してみましょう…:-)
Scalaのfor
内包マップで複数の操作の組成物のための構文糖である、flatMap
とfilter
。またはforeach
。Scalaは実際にfor
-式をこれらのメソッドの呼び出しに変換するため、それらを提供するクラスまたはそのサブセットを理解のために使用できます。
まず、翻訳について話しましょう。非常に単純なルールがあります。
この
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
。これは、コレクションの厳密さに関係なく、常に厳密ではありません。次の例はList
、Scala 2.8の両方のメソッドを示しています。
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の間で非厳密から厳密に変更されました。
withFilter
厳格なコレクションであっても、厳格ではないと想定されているため、説明が必要です。私はこれを検討します...
for(x <- c; y <- x; z <-y) {...}
は次のように翻訳されますc.foreach(x => x.foreach(y => y.foreach(z => {...})))
。2。for(x <- c; y <- x; z <- y) yield {...}
は次のように翻訳されますc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
実際に翻訳されc.map(x => (x, ...)).map((x,y) => {...})
ますか?翻訳されたと思いますc.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
か、何か不足していますか?
はい、Earwickerが言ったように、これselect
はLINQ とほぼ同等であり、RubyやPythonとはほとんど関係がありませんyield
。基本的に、C#ではどこに書き込むか
from ... select ???
Scalaでは代わりに
for ... yield ???
for
-comprehensionsはシーケンスだけでなく、LINQのように特定のメソッドを定義する任意のタイプでも機能することを理解することも重要です。
map
、それはfor
単一のジェネレーターからなる式を許可します。flatMap
と同様に定義する場合は、複数のジェネレータで構成される式をmap
許可しますfor
。foreach
、for
利回りなしのループ(単一および複数のジェネレーターの両方)が許可されます。filter
、それが可能にfor
-filter式が始まるif
でfor
表現。Scalaユーザー(私はそうではありません)からより良い回答を得られない限り、これが私の理解です。
これfor
は、既存のリストから新しいリストを生成する方法を示すで始まる式の一部としてのみ表示されます。
何かのようなもの:
var doubled = for (n <- original) yield n * 2
したがって、各入力に対して1つの出力項目があります(ただし、重複を削除する方法はあると思います)。
これは、他の言語のyieldによって可能になる「命令型の継続」とはかなり異なります。他の言語では、ほとんどすべての構造の命令型コードから任意の長さのリストを生成する方法が提供されます。
(C#に精通している場合は、C#よりもLINQの select
演算子に近くなりyield return
ます)。
キーワードyield
Scalaでは、単にシンタックスシュガーで簡単に置き換えることができるmap
よう、ダニエルソブラルは既に説明を詳細に。
一方、Pythonのジェネレーター(または継続)と同様のジェネレーター(または継続)を探しているyield
場合、は完全に誤解を招きます。詳細については、このSOスレッドを参照してください。Scalaで「yield」を実装するための推奨される方法は何ですか?
次の理解のために考慮してください
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
次のように大声で読むと役立つ場合があります
「についてそれぞれ整数i
、場合それはより大きい3
、次いで収率(生産)i
とリストに追加A
」。
数学的なセットビルダー表記法に関して、上記のfor内包表記は、
これは次のように読むことができます
「についてそれぞれ整数、あれば、それはより大きい、それがメンバーである集合の。」
または代わりに
" は、それぞれがより大きいようなすべての整数のセットです。"
Yieldは、見えないバッファが含まれる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つのコードも同等です。
地図は歩留まりと同じくらい柔軟性があり、逆も同様です。
収量はmap()よりも柔軟です。以下の例を参照してください
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
収量は次のような結果を出力します:List(5、6)、これは良いことです
map()はList(false、false、true、true、true)のような結果を返しますが、これはおそらく意図したものではありません。