[Swift][Combine] Combine で差分数列を作る

Combine を使いこなす一例を紹介します。

Combine を使いこなしている人には、普通かもしれませんが、悩んだので自分メモ代わりに

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Big Sur 11.3
  • Xcode 12.5
  • iOS 14.5

行いたい処理

[1,2,4,7,11,16,22,29] という数列が与えられた時に、その差分の数列を入手したいとします。

欲しい差分数列とは、[1,2,3,4,5,6,7] です。

実装を考えてみる

for 文でループ処理

もちろん for 文を使ってループ処理することで できます。

Combine を使った 実現方法の検討

以下は、Combine を使ってどうやるかを考えてみます。

publisher

Combine を使うということで、配列から publisher を作って、オペレータを連結して解を得るということをしていきます。

配列は、以下のような形で publisher にすることができ、以降にオペレータを追加していくことができます。

コード解説
  1. 配列を publisher 化しています
  2. publisher から、配列として取り出します

Publisher 化して、配列化しているだけなので、計算は何もしていません。

配列要素それぞれを +1 した配列

オペレータを適用することで、処理を追加することができます。
以下は、配列の要素それぞれを +1 するような処理を追加しています。

コード解説
  1. 与えられた値に +1 したものを返すことで、各要素を +1 しています

上記の map オペレータは、その時の値のみが渡されてきています。渡された処理を +1 して返すことで、処理対象の各要素を +1 できます。ですが、その時の値のみが渡されるため、前後の値を参照することができません。

前後の情報を渡してくれるオペレータとしては、reduce や scan があります。

reduce オペレータと scan オペレータ

reduce と scan は、よく似たオペレータです。相違点としては、処理途中の値を都度 emit するか、最後まで処理を行なった時に最終結果を emit するかの違いです。

Apple の reduce についてのドキュメントは、こちら

Apple の scan についてのドキュメントは、こちら

MEMO
Apple のドキュメントは、View Modifier 、Combine operator 共に、検索しづらいです。
operator については、Publishers という publisher (operator は、publisher の1種 です)のための名前空間が用意されているので、このページから必要な Publisher/Operator を探すのが便利です。

scan オペレータを検討する

差分数列を作るためには、要素ごとに、出力が必要となりますので、reduce/scan の出力タイミングを考えると reduce ではなく、scan が候補になります。

scan には 初期値と closure を与えることができます。実際に実行される時には、closure に 前回(closureが)返した値と今回の値が渡されます。

ここで、closure が その時の差分”のみ” を返してしまうと、次回処理時には、その差分のみ の情報しか渡されてこないため、その回での前回値との差分を計算することができません。

そこで、以下のように、差分値 と 値 の 2つをもつ tuple を closure の返り値とすることで 前回差分値のみでなく 前回値も受け取ることができるようになります。

ここまでできれば あとは、Tuple の最初の要素を抜き出して数列を作ることで、目的の 差分の数列を得ることができます。
# 初期値に0を与えて計算しているので、不要であれば最初の要素を削除することが必要です。

# dropFirst を適用したため result が ArraySlice となっています。比較のために XCTAssertEqual の中で、Array 化してます。

まとめ:Combine の活用

Combine の活用
  • .publisher を使うと、配列を Publisher にできる
  • .scan, .reduce を使うと 前回処理時の情報を取得できる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です