LoginSignup
0
0

More than 5 years have passed since last update.

Shapeless で固定長形式の行データをデコードする

Last updated at Posted at 2018-03-04

固定長形式のテキストの一行を、shapeless でケースクラスにマッピングする方法を考えてみた。

動機

Think Stats-プログラマのための統計入門』では、第1章の分析対象が固定長形式のテキストデータとして書かれている(NSFGの'2002FemResp.dat'、ファイル形式はここ)。

固定長形式とは、1レコードを1行とし、各フィールドのデータを固定の開始・終了桁で示した位置に書きこんだもの。

この形式のファイルを読み込む Python コードが、書籍のサイトで提供されているが、同じことを Scala + shapeless で宣言的に書いてみたい。

方針

簡単のために、以下のようなシンプルなケースクラスIceCreamで考えてみる(『The Type Astronaut's Guide to shapeless』から借用)。

case class IceCream(name: String, numCherries: Int, inCone: Boolean)

このインスタンスを、以下のようなフォーマットのテキスト行をデコードして生成してみたい。

  • name: 1桁~12桁
  • numCherries: 13桁~14桁
  • inCone: 15桁~17桁(yes または no)

(shapeless は 2.12の 2.3.3、ソースはここ)

やり方

まず行の指定の位置から、変換関数を用いて値を抽出する関数を返す高階関数extractを、以下のように定義する。

def extract[A](start: Int, end: Int, convert: String => A): String => A =
  line => convert(line.substring(start - 1, end).trim)

これを使ってフィールドごとに抽出関数を作って、関数のHListとする。

val extractors =
  extract(1,  12, identity)   ::
  extract(13, 14, _.toInt)    ::
  extract(15, 17, _ == "yes") ::
  HNil

これを行に適用して変換結果フィールド値のHListを得るには、同じ要素数だけ行を繰り返したHListを作って引数のHListとし、extractorsとはり合わせる形で関数適用する。

指定数だけ行を繰り返して引数のHListとするには以下のような関数を使う。

def repeat[I <: HList, L <: Nat, O <: HList](in: I, s: String)(
  implicit
    len: Length.Aux[I, L],
    rep: Repeat.Aux[String :: HNil, L, O]
): O = rep(s :: HNil)

「関数のHList」を「引数のHList」に適用するには、zipApplyを使い、最後にGenericを使ってIceCreamに変換する。まとめると次のような関数になる。

def lineToIceCream(line: String): IceCream =
  Generic[IceCream].from(extractors zipApply repeat(extractors, line))

以下のように確認できる。

val lines = List (
    "Sundae       1no ",  // IceCream(Sundae,       1,  false),
    "Cornetto    13yes",  // IceCream(Cornetto,     13, true),
    "Banana Split 0no ")  // IceCream(Banana Split, 0,  false)
  .map(lineToIceCream)

所感等

  • 命令型のコーディングだと、フィールド定義をループしながら一個ずつ値を抽出・変換して、結果のオブジェクトに書き込んで更新する形になるが、Scala + shapeless だとかなり宣言的に書ける。

  • IceCreamと同様に NSFG のデータ 13593件も問題なくデコードできる。

  • FS2などを用いてStreaming I/O的にレコードの流れを扱う方法などは、別の機会にやってみたい。

まとめ

型レベルプログラミングも宣言的なコーディングに役に立つ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0