Haskellでdiv/modするときは括弧を忘れずにっていう話

最近プログラミングHaskellを読んでる.(写経している)

専門書は持っていても,マンガとかにどうしても気が向いてしまう+新しいのを買ってしまうので,全部読んだことが少ない.全部読んだのは,"プログラマが知っておくべき97のこと"ぐらい.明らかにお金の無駄遣い.

それはともかく本題.

プログラミングHaskellの第5章に小文字アルファベットで与えられる文を,おなじみのシーザー暗号に変換するプログラムがある.

シーザー暗号に変換するときは剰余をとることが多いが,普通僕が使う言語(C,C++,C#,Javaでは)では,ちょっとした工夫が必要になる.
C,C++,C#,Javaで,a % b とした時,a < 0, b > 0 なら,答えが負の数になるから,それに26を加えるという操作を行う.

でも,この本に書いてあるプログラムでは普通にmod関数を使っているだけでそんなことはやっていない.

負の整数に対する剰余は,競技プログラミングをかじったことがある僕にとっては少し興味があることなので,少し調べてみた.

Google先生に聞いてみると,http://d.hatena.ne.jp/blanketsky/20080610/1213156189が見つかった.ついでに,ローカルを探してGHCiのマニュアルをあさってみたら,

div :: a -> a -> a

integer division truncated toward negative infinity

mod :: a -> a -> a

integer modulus, satisfying

(x `div` y)*y + (x `mod` y) == x

なる記述を見つけた.どうやら,小数として割った答えを負の無限大の方に丸めているらしい.
mod x y は, div x y * y + m = y となるようなmをとっているらしい.

これから,割る数が1以上だったら,modをとると正の数になるはず.

(ここから先は正直書くのが恥ずかしいですが,同じ間違いをした人のために書きます.)

そこで早速GHCiで次の式を打ちこんでみた.

-1 `div` 26   -- 出力 0
-1 `mod` 26   -- 出力 -1

おかしいな.バグってるのかな.と思って,次の書き方に直した.

div -1 26
mod -1 26

両方ともエラーを吐かれた.
どうやら-が曲者らしい.括弧をつける.

div (-1) 26   -- 出力 -1
mod (-1) 26   -- 出力 25

はじめの式を括弧をつけて書き直してみる.

(-1) `div` 26   -- 出力 -1
(-1) `mod` 26   -- 出力 25

つまり,はじめの式はこういう風に解釈,実行されたらしい.

-(1 `div` 26)
-(1 `mod` 26)

演算子よりも,関数適用の方が優先順位が高いことを忘れていたために起きた不幸な事件でした.

プログラミングHaskell

プログラミングHaskell

プログラマが知るべき97のこと

プログラマが知るべき97のこと