Scala for Backend Engineering & Functional Programming · レッスン

reduceとaggregate

要素を結合する別の方法を学びます。

レッスン 4/413 ステップ

「reduceとaggregate」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

foldからreduceへ

別個のシード値を指定せずに要素を結合したい場合があります。

reduceは最初の要素を開始時のアキュムレーターとして使い、残りの要素をそこへ結合します。

要素の型と結果の型が同じ場合に使える、foldを簡略化したものです。

reduceの基本

reduceは二項関数(a, b)を受け取り、明示的な初期値なしでコレクション全体に適用します。

合計であれば、すべての要素を単純に加算します。

val xs = List(1, 2, 3, 4)
val total = xs.reduce((a, b) => a + b)

@main def run(): Unit =
  println(total)  // 10

空のコレクションの落とし穴

reduceにはシードがないため、空のコレクションに対して呼び出すとUnsupportedOperationExceptionがスローされます。

foldとfoldLeftは、指定されたシードを使えるため、空のコレクションでも安全です。

コレクションが空でないと確実に分かっている場合にだけreduceを使用してください。

// List.empty[Int].reduce(_ + _)
// throws UnsupportedOperationException

List.empty[Int].foldLeft(0)(_ + _)  // safe, returns 0

安全のためのreduceOption

reduceOptionはOptionを返し、空のコレクションの場合も例外をスローせずNoneを返します。

空になる可能性がある場合に安全にreduceする方法です。

val xs = List(5, 9, 2)
val maybeMax = xs.reduceOption(_ max _)

@main def run(): Unit =
  println(maybeMax)  // Some(9)

reduceLeftとreduceRight

foldと同様に、reduceにも方向を指定するバリエーションがあります。

reduceLeftは左から右へ、reduceRightは右から左へ結合します。通常のreduceは、並列コレクションでは順序を保証しないため、方向が重要な場合は明示的なバリエーションを使用してください。

val xs = List(1, 2, 3)
val l = xs.reduceLeft(_ - _)   // (1-2)-3 = -4
val r = xs.reduceRight(_ - _)  // 1-(2-3) = 2

@main def run(): Unit =
  println((l, r))  // (-4, 2)

aggregateが存在する理由

foldとreduceでは、アキュムレーター型と要素型が単純な形で関係している必要があります。

aggregateは最も汎用的なfoldです。アキュムレーターを別の型にでき、さらに部分アキュムレーターのマージ方法をScalaに指定できます。これは並列処理で重要になります。

aggregateのシグネチャ

aggregateは、初期値、要素をアキュムレーターに畳み込むseqop、2つのアキュムレーターをマージするcombopを受け取ります。

逐次コレクションではcombopはほとんど使用されませんが、並列コレクションでは各チャンクの結果を結合するために使用されます。

val xs = List(1, 2, 3, 4)
val sum = xs.aggregate(0)(
  (acc, x) => acc + x,   // seqop
  (a, b)   => a + b      // combop
)

@main def run(): Unit = println(sum)  // 10

異なる型でのaggregate

ここではaggregateを使って、単語のリストを1つのInt型の合計文字数に畳み込んでいます。

seqopは各単語の長さを加算し、combopは2つの部分合計を加算します。

アキュムレーター型(Int)は要素型(String)とは異なります。

val words = List("hi", "there", "you")
val chars = words.aggregate(0)(
  (acc, w) => acc + w.length,
  (a, b)   => a + b
)

@main def run(): Unit = println(chars)  // 10

並列集約

aggregateの真価は、並列コレクションで発揮されます。

各スレッドがseqopを使って担当チャンクを畳み込み、その後、combopでチャンクをマージします。この2つの関数によって、Scalaは処理を安全に分割して再結合できます。

// Conceptual: par splits the work
// val n = data.par.aggregate(0)(_ + _.length, _ + _)
// seqop runs per chunk, combop merges chunk results

適切なツールの選択

結果の型が要素型と同じで、結合演算が結合則を満たす場合は、reduceまたはreduceOptionを使用してください。

逐次処理で初期値や異なる結果型が必要な場合は、foldLeftを使用してください。

アキュムレーター型と要素型が異なり、並列処理に適したマージが必要な場合は、aggregateを使用してください。

// reduce:    same type, non-empty, associative
// foldLeft:  seed + different type, sequential
// aggregate: different type + parallel merge

1回の走査での結合

aggregateでは、タプルに蓄積することで複数の値を同時に計算できます。

ここでは1回の走査で合計と個数の両方を求め、combopでタプルをマージしています。

val xs = List(2, 4, 6, 8)
val (s, c) = xs.aggregate((0, 0))(
  (acc, x) => (acc._1 + x, acc._2 + 1),
  (a, b)   => (a._1 + b._1, a._2 + b._2)
)

@main def run(): Unit = println((s, c))  // (20, 4)

理解度チェック

空になる可能性があるリストに対して、どの操作が最も安全かを判断してください。

まとめ

reduceは初期値なしで要素を結合し、最初の要素を開始値として使用します。空のコレクションでは例外をスローしますが、reduceOptionは代わりにNoneを返します。

aggregateは最も汎用的なfoldです。初期値、要素を畳み込むseqop、並列処理のために部分アキュムレーターをマージするcombopで構成されます。

同じ型で結合則を満たす結合にはreduce、逐次的に初期値付きで畳み込む場合はfoldLeft、型が異なる場合や並列処理を行う場合はaggregateを選択してください。

無料で開始

AI チューターと学ぶ Scala — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
39
レッスン
143

よくある質問

「reduceとaggregate」レッスンは無料ですか?

はい。「reduceとaggregate」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

「reduceとaggregate」で何を学びますか?

要素を結合する別の方法を学びます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「reduceとaggregate」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 再帰的に考える
  2. アキュムレーターのパターン
  3. foldLeftとfoldRight
  4. reduceとaggregate
← Scala for Backend Engineering & Functional Programmingに戻る