2013-05-05 6 views
7

Ich habe die folgende Umsetzung der Zip-Funktion in HaskellHaskell Umsetzung von "zip" seltsame Fehler

myzip (a:b) (z:g) 
    | b == [] = [] 
    | g == [] = [] 
    | otherwise = (a,z) : myzip b g 

Wenn ich es in GHCI laden, bekomme ich folgende Fehler

No instance for (Eq b) 
    arising from a use of `==' 
In the expression: g == [] 
In a stmt of a pattern guard for 
       an equation for `myzip': 
    g == [] 
In an equation for `myzip': 
    myzip (a : b) (z : g) 
     | b == [] = [] 
     | g == [] = [] 
     | otherwise = (a, z) : myzip b g 

fehlgeschlagen, Module geladen: keine.

Ich bin mir wirklich nicht sicher, warum das nicht funktioniert. Kann mir jemand Hilfe anbieten?

Antwort

14

Eigentlich ist die Funktion, die Sie in der Frage angegeben haben, in Ordnung. Sie würde bekommen, welchen Fehler Sie zitiert haben, wenn, was hatten Sie war statt:

myzip :: [a] -> [b] -> [(a, b)] 
myzip (a:b) (z:g) 
    | b == [] = [] 
    | g == [] = [] 
    | otherwise = (a, z) : myzip b g 

Mit einer expliziten Art Unterschrift sagen, dass myzip Arbeiten auf Liste der beliebigen Typen a und b. Aber Sie haben b == [] und g == [] verwendet. Der Gleichheitsoperator ist nicht definiert für einen beliebigen Typ, nur für Typen, die ein Mitglied der Klasse Eq sind, so dass der Code, den Sie geschrieben haben, nicht mit dem Typ übereinstimmt, den Sie angegeben haben.

Das ist, was die Fehlermeldung ziemlich direkt sagt, aber wenn Sie nur lernen und noch nicht Klassen aufsteigen, dann ist es ein bisschen unklar.

Wenn Sie die Art Signatur für myzip sagen ändern, dass a und b müssen Mitglieder der Eq Typklasse sein, dann ist der Code, den Sie gab funktioniert:

myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)] 

Oder wenn Sie die Art Signatur hinterlassen ganz aus (wie Sie in der Frage getan haben), leitet GHC diesen Typ tatsächlich von der Tatsache ab, dass Sie den == Operator verwendeten, und der Code kompiliert einfach, wie ist.

jedoch zu prüfen, ob eine Liste leer ist, kann ohne Verwendung des == Führer durchgeführt werden, so können Sie myzip so schreiben, dass es wirklich auf alle Arten a und b funktioniert. Eine Möglichkeit ist es, die null Funktion zu verwenden:

myzip :: [a] -> [b] -> [(a, b)] 
myzip (a:b) (z:g) 
    | null b = [] 
    | null g = [] 
    | otherwise = (a, z) : myzip b g 

Aber ein viel häufiger Weg ist, einfach mehrere Gleichungen verwenden myzip zu definieren, mit Basis Fällen passend mit dem Muster [] und einem Haupt Fall, dass das zu übernehmen erhält der Listen sind nicht leer:

myzip :: [a] -> [b] -> [(a, b)] 
myzip (a:[]) _ = [] 
myzip _ (z:[]) = [] 
myzip (a:b) (z:g) = (a, z) : myzip b g 

Beachten sie, dass dieser Stil auch auf der Hand, dass es gemacht hat, ist ein Fehler in der Implementierung. Sie werfen die letzten a oder z weg, und es gibt keinen Fall für, wenn die Listen vollständig leer sind!

Wenn Ihre Gleichung sagte myzip (a:b) (z:g) und dannb und g gegen die leere Liste überprüft, war es eigentlich zu spät, das Falsche zu überprüfen. Sie müssen nicht überprüfen, ob b[] ist, Sie müssen überprüfen, ob die ganze Liste leer war.Aber Sie hatten bereits angenommen, dass es nicht leer war und zerlegt es in a:b. Dies führt dazu, dass Ihr Code (a) das falsche Ergebnis zurückgibt, weil er das letzte Elementpaar, das es zippen soll, verwirft und (b) einen Fehler erzeugt, wenn eines der Argumente die leere Liste ist.

Rekursion auf Listen sieht normalerweise mehr wie folgt aus:

myzip :: [a] -> [b] -> [(a, b)] 
myzip [] _ = [] 
myzip _ [] = [] 
myzip (a:b) (z:g) = (a, z) : myzip b g 

Dieses korrekt verhält.

+5

Vielen Dank! Ich machte einen Post, ging raus, um etwas zu essen zu essen, und kam zu einer gut geschriebenen und gut durchdachten Antwort zurück. Das Internet ist magisch und die Menschen sind magisch. Du bist magisch! <3 <3 <3 – MYV