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で書いてみる。
過去にBisonのErlang版 Yeccを使ってパーサーを書いたことがあるのですが、作成数ヶ月後にソースコードを読んでみても何をしているのかさっぱり忘れてしまい苦労したことがあります。
おまけにデバッグ大変ですし・・・・
今回はHaskell のattoparsecライブラリを使ってRFC3164のパーサーを書いてみました。
Real world haskellのChapter 16. Using Parsecではattoparsecのベースとなったparsecの説明があるので参考になると思います。
Real world haskellのChapter 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-conduitとattoparsec-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に視覚的にクラスとインスタンスの依存関係マップですっきりと理解。