複数のジェネレーター
ネストした反復処理です。
「複数のジェネレーター」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
ネストした反復を平坦に書く
for内包表記には、複数のジェネレーターを含められます。それぞれはコレクションを反復する<-の行です。複数を組み合わせると、すべての組み合わせが生成されます。これは、ネストしたループを平坦に記述したものと考えられます。
object Main {
def main(args: Array[String]): Unit = {
val pairs = for {
x <- List(1, 2)
y <- List('a', 'b')
} yield (x, y)
println(pairs)
}
}直積になる
サイズがmとnの2つのジェネレーターからは、完全な直積であるm × n個の結果が生成されます。最初のジェネレーターが外側のループ、2番目が内側のループになります。
object Main {
def main(args: Array[String]): Unit = {
val grid = for {
row <- 1 to 3
col <- 1 to 3
} yield s"r$row-c$col"
println(grid.mkString(", "))
}
}内側のジェネレーターは外側に依存できる
後のジェネレーターでは、それより前のジェネレーターで束縛された値を使えます。これは単純な直積では不可能であり、for内包表記が非常に柔軟である理由の一つです。
object Main {
def main(args: Array[String]): Unit = {
val upperTriangle = for {
i <- 1 to 4
j <- i to 4
} yield (i, j)
println(upperTriangle)
}
}3つ以上のジェネレーター
ジェネレーターはいくつでも積み重ねられます。3つのジェネレーターを使うと、3重にネストしたループになり、タプルや計算結果を生成できます。
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 2
b <- 1 to 2
c <- 1 to 2
} yield (a, b, c)
println(triples.size + " combinations")
triples.foreach(println)
}
}古典的なピタゴラス数の例
有名な用途の一つに、ピタゴラス数の組を見つける処理があります。3つの数を反復し、a*a + b*b == c*cを満たすものだけを、複数のジェネレーターとガードを組み合わせて残します。
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 20
b <- a to 20
c <- b to 20
if a * a + b * b == c * c
} yield (a, b, c)
triples.foreach(println)
}
}糖衣構文展開の仕組み
複数のジェネレーターは、最後にmapを置いた、ネストしたflatMapの呼び出しになります。
xs.flatMap(x => ys.flatMap(y => zs.map(z => ...)))
ジェネレーターが1つ増えるたびに、ネストの階層が1つ増えます。
object Main {
def main(args: Array[String]): Unit = {
val sugar = for {
x <- List(1, 2)
y <- List(3, 4)
} yield x * y
val desugared = List(1, 2).flatMap(x => List(3, 4).map(y => x * y))
println(sugar == desugared)
}
}ジェネレーターとガードの組み合わせ
ジェネレーターとガードは自由に交互に配置できます。2つのジェネレーターの間に置いたガードは、内側のループが実行される前に外側の値を取り除きます。
object Main {
def main(args: Array[String]): Unit = {
val result = for {
x <- 1 to 5
if x % 2 == 1
y <- 1 to 5
if y > x
} yield (x, y)
println(result)
}
}ネストしたコレクションを平坦化する
コレクションのコレクションを扱う場合、2つのジェネレーターで自然に平坦化できます。最初のジェネレーターが外側のコレクションを反復し、2番目が各内側のコレクションを反復します。
object Main {
def main(args: Array[String]): Unit = {
val matrix = List(List(1, 2), List(3, 4), List(5, 6))
val flat = for {
row <- matrix
value <- row
} yield value
println(flat)
}
}文字列のグリッドを作る
複数のジェネレーターは、座標や掛け算の表など、構造化された出力の生成に適しています。
object Main {
def main(args: Array[String]): Unit = {
val table = for {
i <- 1 to 3
j <- 1 to 3
} yield s"$i*$j=${i * j}"
println(table.grouped(3).map(_.mkString(" ")).mkString("\n"))
}
}サイズの異なるコレクションを組み合わせる
ジェネレーターの長さをそろえる必要はありません。それぞれから1つずつ要素を選んだ、すべての組み合わせが生成されます。
object Main {
def main(args: Array[String]): Unit = {
val sizes = List("S", "M", "L")
val colors = List("red", "blue")
val products = for {
s <- sizes
c <- colors
} yield s"$c-$s"
println(products)
}
}順序によって出力の並びが変わる
ジェネレーターの順序を入れ替えても組み合わせの集合は同じですが、最後のジェネレーターが最も速く変化するため、順序は変わります。
object Main {
def main(args: Array[String]): Unit = {
val ab = for { x <- List(1, 2); y <- List(9, 8) } yield (x, y)
val ba = for { y <- List(9, 8); x <- List(1, 2) } yield (x, y)
println(ab)
println(ba)
}
}クイックチェック
for { x <- List(1,2,3); y <- List('a','b') } yield (x, y)は、タプルをいくつ生成しますか?
まとめ
複数のジェネレーターについて学びました。
- 各
<-行が、ネストした反復の階層を1つ追加します - 組み合わせると直積が生成されます
- 内側のジェネレーターは、外側で束縛された値に依存できます
- ネストした
flatMapと最後のmapに糖衣構文展開されます - ジェネレーターの順序によって結果の並びが変わります
よくある質問
「複数のジェネレーター」レッスンは無料ですか?
はい。「複数のジェネレーター」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「複数のジェネレーター」で何を学びますか?
ネストした反復処理です。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「複数のジェネレーター」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?
はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- for の脱糖
- if によるフィルタリング
- 複数のジェネレーター
- Option と Future の利用