【Haskell】 インデントで表現された木構造のパース
今日はhaskellネタ。
現在私が務めている会社は、良くも悪くも日本の伝統的な製造業といった感じなので、 今のところ、haskellを製品のソフトウェア開発に使う機会は全くないのですが1、 excelデータ(csvとかtsv)の処理等で、haskellをスクリプト的にを使っています。
今回も普通のサラリーマンの日常業務をhaskellで効率化するのを念頭に、 記事を書いてみます。
普通のサラリーマンが出会うデータは大抵の場合excelデータだと思うので、 以下の様なモデルを考えています。
入力: xlsデータ
出力: xlsデータ
手順
また、日常的に(excelで)扱うデータ形式は、大抵の場合、
の2種類だと思うので、今回は、 インデントで表現された木構造のパース方法について、書いていこうと思います2。
インデントて表現された木構造のパース
indentsパッケージ利用します。
前置きが長くなりましたが、以下がソースコードです。
module IndentedTree where
import Control.Applicative ( (*>) )
import Data.Char (isSpace)
import Data.Tree (Tree (..))
import Text.Parsec (many1, letter, try, option, spaces, char, runParserT)
import Text.Parsec.Indent (withBlock, runIndent, IndentParser(..))
import MyParser (float) -- 浮動小数点をパースする適当なパーサー
type Parser a = IndentParser String () a
parseIndentedTree input = runIndent "" $ runParserT aTree () "" input
aTree :: Parser (Tree (String, Double))
aTree = spaces *> withBlock Node aNodeHeader aTree
aNodeHeader :: Parser (String, Double)
aNodeHeader = do
str <- many1 letter
i <- option 0 (char ':' *> float)
spaces
return (str, i)
-- 入力例
example = unlines [
"foo:100",
" knf:2",
" inf:5",
" enx:15",
" indent",
" dog:4",
" bar:10",
" kobolt",
" wkoiking:6",
" taiwan",
" copilot:50"
実行結果
特に、説明をしなくても、Text.Parsec.Indentモジュールの使い方は明瞭だと思いますが、 一点だけ注意点があります:
withBlock
コンビネータは現在のインデントの深さで、入れ子構造を判断しますが、 それ自身は入力列のスペースを消費しません。したがって、スペースを明示的に読み飛ばすのを 忘れないようにしなければなりません。