Scalaでコレクションを前後処理しつつ走査する

関数型言語らしく書くには

Katsuya Kubo
nextbeat-engineering
5 min readFeb 22, 2019

--

はじめに

こんにちは。nextbeat 開発グループに所属している 久保 です。
nextbeat では保育園向け業務支援サービス KIDSNAコネクト の開発を担当しています。

現在 KIDSNAコネクト では主に保育園の 経営業務 が楽になるような機能を開発しています。
具体的には 労務管理保育料金管理 バックオフィス系 の機能強化です。

連続するデータを1つずつ前後処理する

バックオフィス系の機能はビジネスロジックが複雑になりがちです。
手続き型言語ばかりやってきた自分としては、複雑な処理を関数型ライクに書くことに苦労していました。
つい先日も 連続するデータの差分を1つずつ取得する 処理で「なんとか綺麗に書けないか」と悩んでいました。
その際、諸先輩方にアドバイスを頂きましたので共有しようと思います。

今回はもっと簡素化した、
1年間の月次平均気温データから各月の気温差を取得する処理
を例に見ていきたいと思います。

その1:再帰とmatch式を使う

再帰パターンマッチ を使った関数型言語らしい書き方かと思います。
1つ目のポイントとしては match式 の部分です。

case head +: second +: tail =>

SeqLike な値に対してこのようなパターンマッチを行うと、以下のように評価されます。

case 1つ目の値 +: 2つ目の値 +: それ以降の残りの値 =>

今回の場合、一番最初のループでは以下のように評価されます。

Seq(4, 5, 11, 17, 19, 22, 28, 28, 22, 19, 14, 7) match {
case 4 +: 5 +: (11, 17, 19, 22, 28, 28, 22, 19, 14, 7) =>
f(Seq.empty :+ abs(4 - 5), 5 +: (11, 17, 19, 22, 28, 28, 22, 19, 14, 7))
……

head , second として取り出し、前後処理しています。
そして2つ目のポイントですが再帰関数(f)です。
第1引数は気温差のSeq第2引数は残りの気温Seqを渡しています。
これを繰り返すと、tSeq の中身が1つになった時点で、match式1つ目のパターンにマッチしなくなります。
そして、最終的に今までの結果を集めた diffSeq を返すことで気温差Seqを取得しています。

ちなみに関数fの上にある

@scala.annotation.tailrec

はその下の再帰関数が末尾再帰に最適化されているかどうかを、コンパイル時にチェックするためのアノテーションです。
末尾再帰として最適化されていないとコンパイラがコンパイルエラーとして扱ってくれます。

再帰関数は階層が深くなるとスタックオーバーフローする可能性がありますが、末尾再帰最適になっているとコンパル時にループへ変換されるのでこれを回避する事ができます。なので特殊な理由がない限りアノテーションは付けて末尾再帰にすべきです。

その2:前後の値を1セットにしたコレクションを使う

個人的には再帰処理の方が書き始め易いのですが、やはり最終的に読みやすいのは foldLeft でしょうか。最初は再帰で書いてその後 fold に清書する、というのもアリかもしれません。

まず最初に sliding を使い、先頭から2つずつ要素を取得したコレクションを生成します。sliding の返り値は Iterator[Seq[Int]] です。実際にどんな値が返ってきているか確認するために toListしてみると下記のようになります。

tSeq.sliding(2).toList
// -> res0: List[Seq[Int]] = List(List(4, 5), List(5, 11), List(11, 17), List(17, 19), List(19, 22), List(22, 28), List(28, 28), List(28, 22), List(22, 19), List(19, 14), List(14, 7))

コード上ではSeqのままですが、これを foldLeft で回して、気温差を取得していきます。

まとめ

関数型言語に慣れた方々からすれば大した内容ではなかったかもしれませんが、PHPerだった自分としては目から鱗でした。
今回はその1とその2の違いについてまでは踏み込めませんでしたので、今後はそういったパフォーマンス等の違いも調べたいと思っています。

告知

隔週木曜日に恵比寿のオフィスで夜活(交流会)を実施しています。
転職希望ではなくても当社にご興味をお持ちの方は是非遊びに来てください!
綺麗なオフィスで軽食とお酒を楽しみましょう!

--

--