[Windows][Haskell][emacs]windows環境でのghc-mod設定方法
windows環境でのemacsに対してhaskell-modeとghc-modを適用する方法について。Windows環境だと意外とめんどくさかった。
説明中にある「XXXX」は任意のユーザ名に変換する必要あり。
手順
1.「ghc-mod」のインストール
cabal install ghc-mod cd "C:\Users\XXXX\AppData\Roaming\cabal\ghc-mod-1.11.0" make
2.「config.ini」の設定
gnupackのPATHにWindowsのパスを通す。*1
これを行わないと、ghc-modの呼び出しが失敗する。
gnupackのディレクトリ直下にある「config.ini」を編集する。
↓「%PATH%;」を追加 PATH = %PATH%;%EMACS_DIR%\bin;%INST_DIR%\app\vim;%INST_DIR%\app\script PATH = %PATH%;%INST_DIR%\app\mingw\tdm\bin;%_local_%\bin;%CYGWIN_DIR%\bin PATH = %PATH%;%windir%\system32;%windir%;%windir%\system32\Wbem
3.「haskell-mode」のインストール
https://github.com/haskell/haskell-mode からhaskell-modeをダウンロードし、「~/.emacs.d」以下に配置する。
4.「emacs.el」の設定
emacs.elに下記内容を追記する。
;; haskell-mode (load "~/.emacs.d/haskell-mode/haskell-site-file") (add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode) (add-hook 'haskell-mode-hook 'turn-on-haskell-indent) (add-hook 'haskell-mode-hook 'font-lock-mode) (add-hook 'haskell-mode-hook 'turn-on-haskell-ghci) ;; ghc-mod (add-to-list 'exec-path (concat (getenv "HOME") "c:/Users/XXXX/AppData/Roaming/cabal/bin/")) (add-to-list 'load-path "c:/Users/XXXX/AppData/Roaming/cabal/ghc-mod-1.11.0") (autoload 'ghc-init "ghc" nil t) (add-hook 'haskell-mode-hook (lambda () (ghc-init) (flymake-mode)))
faradayライブラリのwarning
Rubyの1.9にてfaradayライブラリを使うとwarningが出て気持ち悪い件の対処方法について。
現象
使うたびにwarningが出て、大変なことになる。
実際に出てくるwarningメッセージ
/opt/lib/ruby/gems/1.9.1/gems/faraday-0.7.4/lib/faraday/utils.rb:128: warning: rto UTF-8 string
対処方法
「faraday/utils.rb」のとある1行を書き換える。
変更前
def escape(s) s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do '%' << $1.unpack('H2'*bytesize($1)).join('%').tap { |c| c.upcase! } end end
対応後
def escape(s) #↓ここを修正 s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/u) do '%' << $1.unpack('H2'*bytesize($1)).join('%').tap { |c| c.upcase! } end end
gsubの正規表現にて使用している「n」を「u」に変えるだけ。
これでwarningは出なくなる。
対応方法が正しいかはメンドイのでまったく考えていない・・・
レシートをスキャンして日付ごとに分類してみた
ドキュメントスキャナのDR-C125を買ったので、面白い使い道ないかな〜と考えた結果、レシートをスキャンして日付ごとに分類させて見ることにしました。
スキャンさせるだけなら、ものぐさで家計簿つけれない自分でも続くはず。。。
別に合計金額とかも出すわけじゃないので、家計簿用途にはまったく使えないけど、時々昔買ったあの製品いくらだっけ?と思い出せず悩むことがあるので、そういう時には有効かと
ステップ1:スキャン時の設定
適切な設定を探す作業は、分類のためのプログラム書いている時間の数倍かかった。。。
結論としては、
- モード
- アドバンストテキストエンハンスメントII
- 明るさ
- かなり暗く(50くらい?)
- 重送検知
- 超音波検知のみ(長さ検知をしてしまうと、レシートは細長いので誤判定される)
- 読み取り面
- 片面(両面にすると、裏に店名のロゴとかがあるレシートの時問題あり)
- カラードロップアウト
- クレジットカードのレシートとかは紙が青だったり、赤だったりするので読み込む際に適宜設定
な感じ。
ステップ2:プログラム作成
正規表現でパターンマッチングするだけだと思いきや、スキャナの誤検知の尻拭いをしたり、レシートのフォーマットが微妙に違ったりでちょっぴり苦労。
ポイントは
- 「0」を「o」、「1」を「l」と誤検知することが多い
- 年は「2011」と「11」の2種類がある
- 年が省略されて月日のみの場合もある
- 年月日に使用されるセパレータは3パターン
- 2011年8月7日
- 2011/8/7
- 2011-8-7
- 月や日が一桁のときは前に0が入ったりは入っていなかったりする
- ポイントの失効日や広告が日付として入っている場合がある
日付に「2011-8-7」のパターンを認めてしまうと誤検地が一気に上昇する、店の住所が「XX町11-5-6」とかだと完全に区別がつかない・・・
あと何気に「2011/8/7」のパターンもスラッシュが文の中に意外とあるので、変なところが引っかかってしまう。
という訳で信頼度は
- 2011年8月7日
- 2011/8/7
- 2011-8-7
の順となり、この順序で調べていくことになる。
ポイントの失効日や広告の日付は大抵未来の日付なので、プログラムで解析した日付よりも未来の日付を削ってやれば上手くいく。
複数の日付が取れた場合はその中の一番過去の日付を採用してやればいいという考え方もあるが、今回試した限りでは古いほうの日付は誤検知日付であったという例が結構あったので、その対応は行わず。
というわけでプログラム。
今回は、日付の抽出のみで実際にディレクトリごとに仕分けとかは行っていない。
# -*- coding: utf-8 -*- require 'kconv' require 'date' NUM_REG = '[0-9ol]' def to_num(str) str.to_s.tr("ol", "01").to_i end def split_date(txt, ym_sep, md_sep) ret = [] txt.scan(/(?:((?:20)?#{NUM_REG}{2})[#{ym_sep}])?(#{NUM_REG}{1,2})[#{md_sep}](#{NUM_REG}{1,2})(?:日)?[^\-]/u) do |y, m, d| y = 2011 if y==nil y = to_num(y) y += 2000 if y<100 m = to_num(m) d = to_num(d) if Date::exist?(y, m, d) && Date::new(y, m, d) <= Date::today ret << "#{y}/#{m}/#{d}" end end ret end def read_date(filename) ret = [] date = "" IO.popen("xdoc2txt -o=0 -o=1 #{filename}", 'r') do |io| data = io.read.toutf8.gsub(/\s/, "") # puts data ret += split_date(data, '年', '月') ret += split_date(data, '/ノ', '/ノ') if ret==[] ret += split_date(data, '\-', '\-') if ret==[] end ret.uniq end def analyze_date() empty_day = 0 single_day = 0 multiple_day = 0 Dir.glob('*.pdf').each do |file| date_list = read_date(file) case date_list.size when 0 empty_day += 1 when 1 single_day += 1 else multiple_day += 1 end date_list = date_list.join(" ") puts "#{file}:#{date_list}" end puts "empty_day:#{empty_day} single_day:#{single_day} multiple_day:#{multiple_day}" end analyze_date()
ステップ3:解析精度計測
作ったプログラムがどの程度正しく動くか試してみた。
使用データはこの日のためにコツコツと貯めていた124枚のレシートたち。
実行結果
$ ruby pick_out_date.rb レシート20110807132143.pdf:2011/7/8 レシート20110807132145.pdf: レシート20110807132148.pdf:2011/7/21 レシート20110807132150.pdf:2011/7/18 レシート20110807132152.pdf:2011/7/18 レシート20110807132155.pdf:2011/7/22 レシート20110807132159.pdf:2011/7/23 (中略) レシート20110807232317.pdf:2011/1/1 2011/7/31 レシート20110807232321.pdf:2011/1/1 2011/7/31 レシート20110807232322.pdf:2011/7/31 レシート20110807232324.pdf:2011/7/30 レシート20110807232328.pdf:2011/7/30 2011/8/8 レシート20110807232330.pdf:2011/8/7 レシート20110807232332.pdf:2011/7/30 レシート20110807232335.pdf: レシート20110807233227.pdf:2011/7/31 レシート20110807233229.pdf:2011/7/31 レシート20110807233402.pdf:2011/7/31 レシート20110807233403.pdf: レシート20110807233611.pdf:2011/7/31 empty_day:28 single_day:89 multiple_day:7
まとめ
| 日付抽出失敗 | 23% |
| 日付抽出成功 | 72% |
| 日付複数抽出 | 6% |
割といい感じで抽出できている。
ただし、日付抽出に成功したもののうち何%が本当に正しい日付かどうかは確認していない。
本当に正しいかどうかを確認すると50%程度になってしまう気がしている。というのもデータを見渡してみると絶対違うだろと突っ込みたくなるのが少なからずあるため。。。
結論
日付による自動分類は出来なくはないが、若干微妙・・・
もう少し頑張れば行けるかも。
TrashMove v0.9.3
ごみ箱のバグをちょっとずつ修正中〜
以下修正点
- プログラムの実行中にごみ箱の名前を変えるとだめな問題を修正 (あしぃさん不具合報告ありがとうございます。)
- 起動時の初期化処理をちょいと修正
ダウンロードは配布ページから → http://d.hatena.ne.jp/seroron/20110519
Program_optionsをWindows環境で使用する方法
Boost::Program_optionsをWindows環境で使用する方法について。
Program_optionsをWindowsで使おうとすると2つ問題が起こる。
- WinMainにはargc、argvがない
- デフォルトでは「/h」とかのスラッシュから始まるオプションは受け付けてくれない。
argc,argvについては「__argc」、「__argv」(「__wargv」)を使うという手段がある。
または、「boost::program_options::split_winmain」というのが用意されているので、それを使用すればよい。
スラッシュについてはパース時のスタイルを変更してやればよい。
具体的にはboost::program_options::command_line_style::allow_slash_for_short」、
「 boost::program_options::command_line_style::allow_long_disguise」を使う。
まとめるとこんな感じ
using namespace boost::program_options; variables_map vm; int style = command_line_style::unix_style | command_line_style::allow_long_disguise // 「-num=10」のようなスタイルを許可 // (「--num=10」じゃなくてもOKにする) | command_line_style::allow_slash_for_short // 「/」によるオプション開始の許可 ; std::vector<std::wstring> args = split_winmain(cmdline); store(wcommand_line_parser(args).style(style).options(cmdline_desc).run(), vm);
ここでは「unix_style」を使用しているが、これだと「-h」とかの引数も受け付けてしまうため、これを禁止したい場合は「unix_style」を使用せず、自分ですべてオプションを定義してやる必要がある。
「unix_style」は
unix_style = (allow_short | short_allow_adjacent | short_allow_next
| allow_long | long_allow_adjacent | long_allow_next
| allow_sticky | allow_guessing
| allow_dash_for_short)
なので、あとはよしなに・・・
ちなみに、Program_optionsについての記事が検索してもそんなに出てこないんだけど、余り皆使っていないのかな?
自分はこれ結構好きなんだけど。
TrashMove v0.9.2
バグを1件修正しました。これで、動かないといっていた人の何割かは解決するはず。
画面の色数が16bitでは動かないと聞いたとき、なんでやねんと最初は思ったが、よくよく考えてみると確かにそれでは動かないという・・・そんな原因思いつかないよ(ーー;)
報告をしていただいたtmcさん、本当にありがとうございます。
ダウンロードは配布ページから → http://d.hatena.ne.jp/seroron/20110519
