正規表現からの脱出

はじめてのHaskellプログラム http://foobardam.hatenablog.com/entry/2012/07/15/000640 では、正規表現を用いてバッテリー残量プログラムを書いた。

haskellライブラリの正規表現で、=~には感動を覚えた。多相性ってこんな使い方ができるんだと感動ひとしきり。この様子は、siroccoさんの http://d.hatena.ne.jp/sirocco/20090416/1239852340 を参照すると分かる人には分かる。perlのnオプションで処理できるような行指向のテキスト加工処理(つまり、ちょっとしたテキスト加工処理)ならば、これで処理できそうだ。その代表として、バッテリー残量プログラムをあげることができるのではないだろうか。

だがしかし、一方で山本さんの http://d.hatena.ne.jp/kazu-yamamoto/20090309/1236590230 を読む。正規表現は好きなのだが、保守しにくいらしい。簡単な正規表現しか、実際の仕事では書く機会がなかったので、そういうもんなのかなと思ったけど、パーサコンビネータに、興味あったし、自分も正規表現を越えてやろうと思った。まあ、越えるというより脱出という感じ。

題材は、バッテリー残量プログラムとする。それでも、Haskell初心者(start haskellの簡単な練習課題に苦労するようなスキル)の自分には、かなり無謀な挑戦だった。モナドの理解も十分進んでいないうちに、Parsecに、いきなり挑戦してみた。結局、1週間も悪戦苦闘したことになる。

その結果が、これ。

 

import System.IO
import Text.Parsec
import Text.Parsec.String
 
 
getCapacitance :: String -> String -> Double
getCapacitance ptn content = either (\_ -> -1.0) (\x -> x) $ parse (capacitanceP ptn) "" content
 
capacitanceP :: String -> Parser Double
capacitanceP str = do
  manyTill anyChar (try (string str))
  spaces
  n <- many1 digit
  return $ read n
 
main = do
  state <- readFile "/proc/acpi/battery/BAT0/state"
  let current = getCapacitance "remaining capacity:" state
  info <- readFile "/proc/acpi/battery/BAT0/info"
  let full = getCapacitance "design capacity:" info
  putStr $ show (floor $ current / full * 100) ++ "%\n"

 

https://github.com/foobardam/practice-haskell/blob/57137863519521c88e3b9bb4dba15d88ff14fb87/batWoRegex.hs

かなり無理やり何とか書いた。Parsec2とParsec3の違いや、Parsecのソースコードを見て、モナド変換子をまだ理解していない段階で、頭が混乱し、Eitherをどうやって扱うか分からない状態で、無理やりEitherからRight値を取り出したりと、かなりひっちゃかめっちゃかだが、目的だけは達成できた苦労の結晶である。

結論は、状況に応じて臨機応変に、正規表現を使うときは使うしParsecで行くときは行くという柔軟な姿勢が一番良いようだ。