Decimal と binary

いやはや。
こちらのblogみてまた思い出してしまった。

http://www.whitehackerz.jp/blog/?p=1154

浮動小数点数の仕組みをみんな知っているという前提で評価するならいいのですが・・・・
整数、有理数などデータの型ってなに?となったら目も当てられません。

数ヶ月前に、あるプログラマ(前職では課金システムを専門に作っていたらしく、最近定年退職した方)が、隣で課金システムを作っているので、ちょっとデータベース設計とかプログラムを見てみたら 消費税計算をdoubleで処理していて、値をdoubleとしてデータベースに保存してたり・・・・

思わず突っ込みました。 いままでDecimalとか知らないで40年間プログラム作ってたようです。

小数点を含む消費税計算の四捨五入処理とか小数点切り捨てとかで1円とかずれたりする。>はい私経験者ですが。

ここ読めば詳しく書いてある。
http://ja.wikipedia.org/wiki/IEEE_754


金融関係のプログラムを作ったことがある人なら、わかると思うけど、債券とかの取引システムは金額が1件100億とか50億とか大きい数値と金利をdouble、floatなどとして金利計算すると、手数料とかまずいことになります。※為替でもおなじですが。

なのでDecimalで処理するのが当たり前

※POWER6は。ハードウェアベースの10進浮動小数点演算ユニットが組み込まれているらしい。

たぶんその人の作った過去の課金プログラムはまだどこかで、動いていると思うので誤請求していないことを祈りたいです。

Syslog Parserを haskellで書いてみる。

過去にBisonErlangYeccを使ってパーサーを書いたことがあるのですが、作成数ヶ月後にソースコードを読んでみても何をしているのかさっぱり忘れてしまい苦労したことがあります。
おまけにデバッグ大変ですし・・・・

今回はHaskell のattoparsecライブラリを使ってRFC3164のパーサーを書いてみました。

Real world haskellChapter 16. Using Parsecではattoparsecのベースとなったparsecの説明があるので参考になると思います。

Real world haskellChapter 27. Sockets and Syslogでもsyslog serverの実装方法が取り上げていますが、ただ単にsyslog serverが受信したデータをputStrLnで表示しているだけなので、少々素っ気ないです。

RFC3164詳細はこちら
http://www.ietf.org/rfc/rfc3164.txt

まずはReal world haskellの- file: ch27/SyslogTypes.hs を少し変更

module SyslogPriority 
	(
	  Severity
	, Facility
	, codeOfFac
	, facOfCode
	, codeOfPri
	, priOfCode
	) where

data Severity = DEBUG
              | INFO
              | NOTICE
              | WARNING
              | ERROR
              | CRITICAL
              | ALART
              | EMERGENCY
                deriving (Show, Eq, Read, Enum, Ord)

priToCode :: [(Severity, Int)]
priToCode = [
             (DEBUG, 7),
             (INFO, 6),
             (NOTICE, 5),
             (WARNING, 4),
             (ERROR, 3),
             (CRITICAL, 2),
             (ALART, 1),
             (EMERGENCY, 0)
            ]
    
codeOfPri :: Severity -> Int
codeOfPri p = case lookup p priToCode of
                Just x -> x
                _ -> error $ "Internal error in priOfCode"

priOfCode :: Int -> Severity
priOfCode p = case lookup p $ codeTo priToCode of
                Just x -> x
                _ -> error $ "Internal error in codeOfPri"
                 
data Facility = KERN
              | USER
              | MAIL
              | DAEMON
              | AUTH
              | SYSLOG
              | LPR
              | NEWS
              | UUCP
              | CRON
              | AUTHPRIV
              | FTP
              | NTP
              | LOGAUDIT
              | LOGWARNING
              | CLOCKDAEMON
              | LOCAL0
              | LOCAL1
              | LOCAL2
              | LOCAL3
              | LOCAL4
              | LOCAL5
              | LOCAL6
              | LOCAL7
                deriving (Show, Eq, Ord)
                
facToCode :: [(Facility, Int)]
facToCode = [
             (KERN, 0),
             (USER, 1),
             (MAIL, 2),
             (DAEMON, 3),
             (AUTH, 4),
             (SYSLOG, 5),
             (LPR, 6),
             (NEWS, 7),
             (UUCP, 8),
             (CRON, 9),
             (AUTHPRIV, 10),
             (FTP, 11),
             (NTP, 12),
             (LOGAUDIT, 13),
             (LOGWARNING, 14),
             (CLOCKDAEMON, 15),
             (LOCAL0, 16),
             (LOCAL1, 17),
             (LOCAL2, 18),
             (LOCAL3, 19),
             (LOCAL4, 20),
             (LOCAL5, 21),
             (LOCAL6, 22),
             (LOCAL7, 23)
            ]

codeOfFac :: Facility -> Int
codeOfFac f = case lookup f facToCode of
                Just x -> x
                _ -> error $ "Internal error in codeOfFac"

facOfCode :: Int -> Facility
facOfCode f = case lookup f $ codeTo facToCode of
                Just x -> x
                _ -> error $ "Internal error in facOfCode"

codeTo :: [(x, y)] -> [(y, x)]
codeTo = map (\(x, y) -> (y, x))

そしてパーサー RFC3164.hs

{-# LANGUAGE OverloadedStrings #-}

module RFC3164 
    (
      parseRFC3164
    , RFC3164(..)
    , PRI(..)
    , Header(..)
    , Msg(..)
    ) where

import SyslogPriority
import Control.Applicative
import Data.Attoparsec as P
import qualified Data.Attoparsec.Char8 as P8
import Data.Attoparsec.Char8 (char8, endOfLine)
import qualified Data.ByteString.Char8 as B hiding (map)


{-
http://www.ietf.org/rfc/rfc3164.txt
-}

-- SYSLOG-MSG      = PRI SP HEADER SP MSG
data RFC3164 = RFC3164
    {
      pri :: PRI
    , header :: Header
    , msg :: Msg
    } deriving (Eq, Ord, Show)

-- | parser for rfc 3164
-- >>> :set -XOverloadedStrings 
-- >>> parseTest parseRFC3164 "<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8\n "
-- Done " " RFC3164 {pri = PRI {severity = CRITICAL, facility = AUTH}, header = Header {timestamp = "Oct 11 22:14:15", hostName = "mymachine"}, msg = Msg {tag = Just "su", procId = Nothing, content = " 'su root' failed for lonvick on /dev/pts/8"}}
--
-- >>> parseTest parseRFC3164 "<0>Oct 22 10:52:12 scapegoat apache[2321]: 10.1.2.3 sched[0]: That's All Folks!\n "
-- Done " " RFC3164 {pri = PRI {severity = EMERGENCY, facility = KERN}, header = Header {timestamp = "Oct 22 10:52:12", hostName = "scapegoat"}, msg = Msg {tag = Just "apache", procId = Just 2321, content = " 10.1.2.3 sched[0]: That's All Folks!"}}
--
parseRFC3164 :: Parser RFC3164
parseRFC3164 = do
    pri' <- parsePRI
    header' <- parseHeader
    msg' <-  parseMsg
    return $ RFC3164 pri' header' msg'
 
-- PRI = '<' INT '>'
-- INT = 1*3 DIGIT ; range 0 .. 191
--
data PRI = PRI {
	  severity :: Severity
	, facility :: Facility
	} deriving (Eq, Ord, Show) 

-- | facrity and priority check
--
-- Example:
--
-- >>> parseTest parsePRI "<0> "
-- Done " " PRI {severity = EMERGENCY, facility = KERN}
-- 
-- >>> parseTest parsePRI "<191> "
-- Done " " PRI {severity = DEBUG, facility = LOCAL7}
--

parsePRI :: Parser PRI  
parsePRI = do
    pri' <- char8 '<' *> P8.decimal <* char8 '>'
    return $ PRI (parseSeverity pri') (parseFac pri') 

parseFac :: Int -> Facility
parseFac pri' = facOfCode $  pri' `div` 8
    
parseSeverity :: Int -> Severity
parseSeverity pri' = priOfCode $  pri' `mod` 8

-- HEADER          = TIMESTAMP SP HOSTNAME
-- TIMESTAMP = MMM SP DD SP HH COL MM COL SS
-- MMM       = 3PRINTUSASCII  ; Jan Feb Mar Arp May Jun Jul Aug Sep Oct Nov Dev
-- DD        = 2DIGIT  ; <SP>1-28, <SP>1-29, <SP>1-30, <SP>1-31
-- HH        = 2DIGIT  ; 00-23
-- MM        = 2DIGIT  ; 00-59
-- HOSTNAME  =  IPv4 | IPv6 | PRINTUSASCII/DIGIT
-- IPv4      = 3NONZERO-DIGIT DOT 3NONZERO-DIGIT DOT 3NONZERO-DIGIT
-- IPv6     = 

data Header = Header 
    {
       timestamp :: !B.ByteString
     , hostName :: !B.ByteString
     } deriving (Show, Eq, Ord)

-- | header parser check
--
-- example:
--
-- >>> parseTest parseHeader "Feb 12 19:27:01 hostname "
-- Done "" Header {timestamp = "Feb 12 19:27:01", hostName = "hostname"}
--
-- >>> parseTest parseHeader "Feb  2 19:27:01 hostname "
-- Done "" Header {timestamp = "Feb  2 19:27:01", hostName = "hostname"}
--
parseHeader :: Parser Header
parseHeader = do
    time <- P8.take 15 <* char8 ' ' 
    host <- takeTill P8.isHorizontalSpace <* char8 ' '
    return $ Header time host
    
data Msg = Msg {
	  tag :: Maybe B.ByteString
	, procId :: Maybe Int
	, content :: !B.ByteString
	} deriving (Eq, Ord, Show)

--MSG             = TAG COL CONTENT
--TAG		  = 1*32 PROCNAME [ PID ]:
--PROCNAME	  = PRINTUSASCII / DIGIT
--TAGID           = DIGIT  
--CONTENT         = PRINTUSASCII
--SP              = %d32
--PRINTUSASCII    = %d33-126
--NONZERO-DIGIT   = %d49-57
--DIGIT           = %d48 / NONZERO-DIGIT
--DOT		  = %46
--COL		  = %58
--[		  = %91
--]		  = %93

-- | msg parser check
--
-- example:
--
-- >>> parseTest parseMsg "app[12]: message starting \n" -- "12]: message starting \n"
-- Done "" Msg {tag = Just "app", procId = Just 12, content = " message starting "}
--
-- >>> parseTest parseMsg "app: message starting \n" --"message starting \n" 
-- Done "" Msg {tag = Just "app", procId = Nothing, content = " message starting "}
--
--

parseMsg :: Parser Msg
parseMsg = do
    tag' <- Just <$> try (takeTill (\c -> inClass "[:" c)) <|> pure Nothing <$> char8 '['
    pid' <- Just <$> try (char8 '[' *> P8.decimal <* char8 ']' <* char8 ':') <|> pure Nothing <$> char8 ':'
    content' <- takeTill P8.isEndOfLine <* endOfLine
    return $ Msg tag' pid' content'

doctest(ghc7.4.2)で動作を確認
※doctestがない場合は "cabal install doctest"でインストールください

$ doctest RFC3164.hs 
There are 4 tests, with 9 total interactions.
Examples: 4  Tried: 4  Errors: 0  Failures: 0

network-conduitattoparsec-conduitを参考にsyslog server(collector)を実装してみるとおもしろいかもしれません。

bashの実行コマンドをsyslogに記録

bash 4.x から bash command historyをsyslog に記録することができるようになりました。
ただしソースをGNUからDLし少々の設定とコンパイルが必要となります。

ソースの取得

wget http://ftp.gnu.org/gnu/bash/bash-4.2.tar.gz

syslogにコマンドを記録するため設定

ref: http://tiswww.case.edu/php/chet/bash/NEWS

l. There is a new configuration option (in config-top.h) that forces bash to
forward all history entries to syslog.

$vi config-top.h
変更前
/* Define if you want each line saved to the history list in bashhist.c:
bash_add_history() to be sent to syslog(). */
/* #define SYSLOG_HISTORY */
#if defined (SYSLOG_HISTORY)
# define SYSLOG_FACILITY LOG_USER
# define SYSLOG_LEVEL LOG_INFO
#endif

変更後
/* Define if you want each line saved to the history list in bashhist.c:
bash_add_history() to be sent to syslog(). */
/* #define SYSLOG_HISTORY */
#define SYSLOG_HISTORY
#if defined (SYSLOG_HISTORY)
# define SYSLOG_FACILITY LOG_USER
# define SYSLOG_LEVEL LOG_INFO
#endif

コンパイル

./configure
make -j2

確認
./bash
tail /var/log/messege

2012-07-24T13:59:43.208391+09:00 hostname bash: HISTORY: PID=15980 UID=0 tail /var/log/messages

しっかりとコマンド履歴が確認できます。

rsyslogなどでcollector へ送信するといつ誰が何を行ったか記録が残るので、ドキュメント作成、調査などいろいろ便利です。

cabal: Couldn't read cabal file "silently/1.2/silently.cabal"

Mac envでHaskell web framework yesod をインストールしようとすると

$cabal install yesod
cabal: Couldn't read cabal file "silently/1.2/silently.cabal"
cabal: Couldn't read cabal file "bytestring/0.9.2.1/bytestring.cabal"

とエラーが出る

なのでgnu versionのtar をインストールして
brew install gnu-tar

cat ~/.cabal/config |grep cache

ディレクトリにあるパッケージtarファイルの中身のいじる

gnutar vf 00-index.tar --delete bytestring/0.9.2.1/bytestring.cabal
gnutar vf 00-index.tar --delete silently/1.2/silently.cabal

で再度インストール実行
$cabal install yesod

解決!

TokyoWebmining の勉強会初参加

本日とても内容の濃い議論が交わされた勉強会

インプットのあとはアウトプット

ということで

jaccard 係数 http://en.wikipedia.org/wiki/Jaccard_index

先人がすでにライブラリを公開していないか調べてみた。

haskell jaccard で検索すると・・・ありました。

http://hackage.haskell.org/packages/archive/nlp-scores/0.2.2/doc/html/NLP-Scores.html

NLP.ScoresのなかにJaccard coefficient J(A,B) = |AB| / |A union B|関数を発見

さっそく発見したライブラリを利用してちょっと遊んでみます。

前提
AさんとIさんの関係は、友達の友達

Aさんの友人は ['b','c','d','e']
Iさんの友人は ['b','c','d']
とすると・・いかに

module Jaccard where

import NLP.Scores
import Data.Set

a = fromAscList ['b','c','d','e']
i = fromAscList ['b','c','d']

main = do
jaccard a i

AVG in Haskell

haskell で 平均を求める関数を書いてみようと思いったところ、型の制約に四苦八苦。

最初に思いついた関数

avg :: [a] -> a
avg xs = sum xs / length xs

で型のチェックをすると・・・
1 sec2.hs|13 col 19 error| Couldn't match type `a' with `Int'
2 || `a' is a rigid type variable bound by
3 || the type signature for avg :: [a] -> a
4 || at /Users/sec2.hs:13:1
5 || In the return type of a call of `length'
6 || In the second argument of `(/)', namely `length xs'
7 || In the expression: sum xs / length xs

と怒られた・・・

なので早速型を見てみると

(/) :: Fractional a => a -> a -> a
length :: [a] -> Int
sum :: Num a => [a] -> a

みんな型が違うことに気づく・・・

avg :: (Fractional a) => [a] -> a
avg xs = sum xs / fromIntegral (length xs)

と書いて、

ghci> avg [1,2,3]
2.0
ghci>
と値が返ってきたが、何か腑に落ちない。

なのでネットで探すと同じようにはまっている先人を発見
http://stackoverflow.com/questions/2376981/haskell-types-frustrating-a-simple-average-function

ほかにも各クラスやインスタンスの関係があるのか調べてみた。
http://www.sampou.org/haskell/report-revised-j/basic.html

図の6.1に視覚的にクラスとインスタンスの依存関係マップですっきりと理解。