wkoikingのブログ

内容は主にhaskellやxyzzy関連です。

【Haskell】 インデントで表現された木構造のパース

今日はhaskellネタ。

現在私が務めている会社は、良くも悪くも日本の伝統的な製造業といった感じなので、 今のところ、haskellを製品のソフトウェア開発に使う機会は全くないのですが1、 excelデータ(csvとかtsv)の処理等で、haskellをスクリプト的にを使っています。

今回も普通のサラリーマンの日常業務をhaskellで効率化するのを念頭に、 記事を書いてみます。

普通のサラリーマンが出会うデータは大抵の場合excelデータだと思うので、 以下の様なモデルを考えています。

入力: xlsデータ

出力: xlsデータ

手順

  1. 直接コピペか「ファイルの種類の変更」でxlsをtsvデータに変換
  2. haskellで処理
    1. haskellでtsvデータをパース
    2. haskellでデータを処理
    3. tsvで出力
  3. excelに出力tsvをコピペ(おわり)

また、日常的に(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コンビネータは現在のインデントの深さで、入れ子構造を判断しますが、 それ自身は入力列のスペースを消費しません。したがって、スペースを明示的に読み飛ばすのを 忘れないようにしなければなりません。

参考にしたサイト

おまけ

  • MyParser
  • ちゃんと前置きのtsv

    1. パース(parseIndentedTree)して、
    2. 適当な処理(bottomUp, topDown)をして、
    3. tsvで出力(showTree)

    するコード


  1. そもそも私はプログラマではありません。?

  2. 表形式のパースは、比較的簡単なので、省略します。?

Pandocで脱Word

職場でWordを使って文章を書いていると、どうもSAN値が低下するので、 markdownで高品質な文章が書ける環境を模索しました。

その結果、Pandoc + xyzzyで快適に文章が書けるようになった1ので、 紹介したいと思います。

ちなみに、環境はWindows XP or Windows 7です。

Pandocとは

haskellという言語で書かれたドキュメント・コンバータです。

かなり多様なファイル形式に対応していますが、基本的に

  • markdown → html
  • markdown → (latex) → pdf
  • markdown → docx

といった感じでmarkdownでhtml文章や紙媒体の文章を書くためのツールだと思っています。

pandoc markdown

Pandocはpandoc markdownという独自のmarkdown記法を定義しています。

何ができるのか
  • 見出しとか、
  • 箇条書きとか、
  • 表とか、
  • 図とか、
  • 数式とか、
  • コードとか、
  • 目次の自動生成とか、
  • 図表番号参照とか、

要するに文章書くのに必要なことはだいたいできます。

ちなみに、このブログの記事はPandoc Markdownで書かれています:

サンプル用に書いたhtml文章と紙媒体の文章です:

参考サイト

導入にいたっては、以下のサイトを参考にしました。

  • Pandocのインストール
  • 日本語の環境構築

    htmlはテキスト・エディタのエンコーディングをutf-8にすればOKです。

    紙媒体(texによる組版)へのコンパイルはlualatexを使いました。

まずないと思いますが、需要があれば、環境構築など、記事を 書こうと思います。


  1. まだ、docxを使わざるを得ない時も多々ありますが。?

KaMailV3で業務効率を改善

学生の頃からかれこれ5年以上、xyzzy を使い続けているのですが、 特に最近、KaMailV3 で色々捗っているので、 KaMailV3導入時に参考にしたサイトをまとめておきます。

ちなみに、私がKaMailV3導入に成功した環境は以下の3つです。

KaMailV3の導入

作者の服部さんサイト の「インストール」及び「設定」に従います。

注意点
  • ここの拡張lispを何個か別にインストールする必要がある。
  • メーラーの基本的な設定はconfig.l1のコメントを参考にする。

Exchange Server

フリーソフトDavMail Gatewayを使用して、Outlook Web Access経由でメールを送受信します。

Gmail

POPFile経由でGMailをKaMailV3で読むを参照。

KamailV3関連拡張lisp

以下の拡張lispをインストールするとさらに幸せになると思います。


  1. site-lisp/kamail3/config.l.sample を ~/.kamail3/config.l にコピーして作る?

初めての記事

はじめてのの記事(テスト)

ここに本文1がきます。

ヘッダー2

ここが本文2です。

以下、コードの挿入です。

module FFT where

import Control.Arrow ( (***) )
import Data.Complex (Complex, cis)
import Data.List (partition)

type Signal = [Complex Double]

-- 信号処理の基礎的な演算
fft, ifft :: Signal -> Signal
fft = fft' 1
ifft = fft' (-1)

fft' m ls = fft'' m (fromIntegral $ length ls) ls

fft'' :: Double -> Double -> Signal -> Signal
fft'' m 1 ls = ls
fft'' m n ls
  = zipWith3 (\e c o -> (e + c * o) / sqrt 2) (es ++ es) cs (os ++ os)
 where
   (es,os) = dup (fft'' m n') $ ipartition (\k _ -> even k) ls
   cs = [cis (m * 2 * pi * k / n) | k <- [0..(n-1)]]
   n' = scaleFloat (-1) n

ipartition :: (Int -> a -> Bool) -> [a] -> ([a], [a])
ipartition f = dup (map snd) . partition (uncurry f) . zip [0..]

dup f = f *** f

(<**>) :: Signal -> Signal -> Signal
x <**> y = map (/ n) $ ifft $ zipWith (*) (fft x) (fft y)
 where
   n = fromIntegral $ length x

箇条書きのテストです。

画像の挿入。

sakura

sakura

markdown-mode.l

Unko
RightLeftCenterDefault
12 12 12 12
123 123 123 123
1 1 1 1

数式


 f(n)=\sum_{k=1}^n a_k

引用

これは、引用された文章です。