2026-08-23
CompressLoop.hs
compress :: Eq a => [a] -> [a]
compress whole@(x:rest@(y:_))
| x == y = compress whole
| otherwise = x : compress rest
compress xs = xs
main :: IO ()
main = print (compress [1, 1, 2] :: [Int])
compressは連続する重複要素を1つに畳むつもりで、compress [1, 1, 2]は[1,2]を出すはずですが、実際にはmainを実行しても何も出力されず、固まったままになります。何が問題で、どう直せばよいでしょうか?
Answer
whole@(x:rest@(y:_))というas-patternは、xやyと同時に「マッチした部分全体」をwholeとして、「その末尾」をrestとして、2つの名前を作ります。x == yのとき先へ進めるはずがcompress wholeと再帰していて、これは今の呼び出しとまったく同じ引数で自分自身を呼び直しているだけです。リストは1要素も減らないので、x == yである限り(この入力では最初の1 == 1の時点でずっと)前進せず、無限ループになります。同じas-patternでも、進めるべきなのは要素を1つ消費した後ろ側のrestであって、マッチする前の全体であるwholeではありません:
compress :: Eq a => [a] -> [a]
compress (x:rest@(y:_))
| x == y = compress rest
| otherwise = x : compress rest
compress xs = xs