実際、質問で指定した関数は正常にコンパイルされます。あなたが持っていたものが代わりにあった場合、引用したエラーが発生します:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| b == [] = []
| g == [] = []
| otherwise = (a, z) : myzip b g
任意の型myzip
のリストで機能することを示す明示的な型シグネチャを使用して、. しかし、あなたは と を使用しました。等値演算子はどの型にも定義されておらず、型クラスのメンバーである型に対してのみ定義されているため、記述したコードは指定した型と一致しません。a
b
b == []
g == []
Eq
これは、エラー メッセージが非常に率直に言っていることですが、型クラスを学習したばかりで、まだ型クラスに慣れていない場合は、少し不明確です。
それをmyzip
言うために型シグネチャを変更し、型クラスのメンバーである必要がある場合、指定したコードは機能します。a
b
Eq
myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)]
または、型シグネチャを完全にオフのままにしておくと (質問で行ったように)、GHC は演算子を使用したという事実から実際にこの型を推測し==
、コードはそのままコンパイルされます。
ただし、リストが空かどうかのチェックは==
演算子を使用しなくても実行できるmyzip
ため、実際に任意の型a
およびに対して動作するように記述できますb
。1 つの方法は、次のnull
関数を使用することです。
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| null b = []
| null g = []
| otherwise = (a, z) : myzip b g
しかし、もっと一般的な方法は単純に複数の式を使って を定義することですmyzip
。ベースケースはパターンと一致し[]
、メインケースはリストが空でないと仮定します:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:[]) _ = []
myzip _ (z:[]) = []
myzip (a:b) (z:g) = (a, z) : myzip b g
このスタイルは、実装にバグがあることも明らかにしていることに注意してください。a
あなたは最後のorを捨てておりz
、リストが完全に空であるというケースはありません!
あなたの方程式が言っmyzip (a:b) (z:g)
て、空のリストに対してチェックしたとき、それは実際には遅すぎて間違ったことをチェックしていました。isかどうかを確認する必要はありません。リスト全体が空かどうかを確認する必要があります。しかし、あなたはそれが空ではないとすでに想定しており、それを に分解しました。これにより、コードは (a) 圧縮する必要がある要素の最後のペアを破棄するため、間違った結果を返し、(b) 引数の 1 つが空のリストの場合にエラーを生成します。b
g
b
[]
a:b
通常、リストの再帰は次のようになります。
myzip :: [a] -> [b] -> [(a, b)]
myzip [] _ = []
myzip _ [] = []
myzip (a:b) (z:g) = (a, z) : myzip b g
これは正しく動作します。