Haskellの修行のツモリで、Paizaのスキルチェックの過去問題(一番易しいDランク問題)やAtcoder Beginner Contestの過去問題(やはり一番易しいA問題)を解いていた。そしたら、ものの見事にハマった~(涙)。
やりたいことは簡単だったのだ。a、b、x、yといった数値が与えられるので、「2 × a」と「b × (x - y)」の「小さい方」を求めたかった。それでこんなコードを書いてしまった。
min (2 * a) b * (x - y)
変数の値は、
a = 200
b = 300
x = 8
y = 6
とかそんな感じ。変数の値を数値で示すと、
min (2 * 200) 300 * (8 - 6)
こんな感じで、「2×200」=「400」と「300 ×(8 - 6)」=「600」の小さい方を答えて貰いたかった。当然答えは「400」だ。ところが…、何度実行しても、「600」と出力されるのだ!
仕方ないので、
if (2 * a) < b * (x - y) then (2 * a) else b * (x - y)
と書いてみたら…、もちろん「400」が返ってくる。それどころか、
minimum [(2 * a), b * (x - y)]
これだって正しく動作する。それなのに…
min (2 * a) b * (x - y)
これだけが、おかしな値を返してよこすのだ。
…結局、自分の勘違いに気付いたのは20分後か30分後か…。「(2 * a)」のカッコを外してみたのかな? そうしたら思いがけなくエラーが出てしまい、その瞬間に「あ、」と気付いたように思う。
Haskellでは関数適用の優先度がかなり高いので、
min (2 * a) b * (x - y)
こう書くと、
(min (2 * a) b) * (x - y)
こう書いたのと同じことになっちゃうんですね!
これでは、「2×200」=「400」と「300」の小さい方(当然「300」)に「(8 - 6)」=「2」をかけちゃうワケで…、このテのミスをすると普通は一見してオカシな答えが出てくるからすぐ気が付くんだけど、これがまた運の悪いことに、偶然の一致で出てくる(誤った)答えも「600」! 出力されていた「600」は関数適用の(直接の)結果ではなく、関数適用の結果に更に別の値をかけたものだったのか…。
これって「Haskellあるある」なんだろうなぁ。ハマった~!