2010-04-28 4 views
6

Also schreibe ich ein Programm, das eine Prozedur für ein gegebenes arithmetisches Problem zurückgibt, also wollte ich ein paar Funktionen in Show einfügen, damit ich den gleichen Ausdruck ausgeben kann, den ich auswerte, wenn ich Prüfung. Das Problem ist, dass der angegebene Code (-) der ersten Zeile entspricht, wenn er auf die zweite fallen sollte.Seltsame Mustererkennung mit Funktionen Instanziierung Zeige

{-# OPTIONS_GHC -XFlexibleInstances #-} 

instance Show (t -> t-> t) where 
show (+) = "plus" 
show (-) = "minus" 

main = print [(+),(-)] 

kehrt

[plus,plus] 

mich um mich nur eine Todsünde Druckfunktionen an erster Stelle oder gibt es eine Art, wie ich es richtig passen bekommen?

edit: Ich weiß, ich bin die folgende Warnung erhalten:

Warning: Pattern match(es) are overlapped 
     In the definition of `show': show - = ... 

Ich weiß immer noch nicht, warum es überlappt, oder wie es zu stoppen.

Antwort

9

Als sepp2k und MtnViewMark sagte, kann man nicht Mustererkennung auf den Wert der Identifikatoren, nur auf Konstrukteuren und in einigen Fällen, implizite Gleichheit überprüft. Ihre Instanz bindet also ein beliebiges Argument an den Bezeichner und überschreibt dabei die externe Definition von (+). Leider bedeutet dies, dass das, was Sie versuchen, nicht funktionieren wird und nicht funktionieren kann.

Eine typische Lösung für das, was Sie erreichen möchten, ist die Definition eines algebraischen Datentyps "arithmetischer Ausdruck" mit einer entsprechenden show Instanz. Beachten Sie, dass Sie Ihren Ausdruckstyp selbst zu einer Instanz von Num machen können, wobei numerische Literale in einen "Literal" -Konstruktor eingeschlossen sind und Operationen wie (+) ihre Argumente zusammen mit einem Konstruktor für die Operation zurückgeben. Hier ist ein kurzes, unvollständiges Beispiel:

data Expression a = Literal a 
        | Sum (Expression a) (Expression a) 
        | Product (Expression a) (Expression a) 
        deriving (Eq, Ord, Show) 

instance (Num a) => Num (Expression a) where 
    x + y = Sum x y 
    x * y = Product x y 
    fromInteger x = Literal (fromInteger x) 

evaluate (Literal x) = x 
evaluate (Sum x y) = evaluate x + evaluate y 
evaluate (Product x y) = evaluate x * evaluate y 

integer :: Integer 
integer = (1 + 2) * 3 + 4 

expr :: Expression Integer 
expr = (1 + 2) * 3 + 4 

es Ausprobieren in GHCi:

> integer 
13 
> evaluate expr 
13 
> expr 
Sum (Product (Sum (Literal 1) (Literal 2)) (Literal 3)) (Literal 4) 
+0

Das ist fantastisch, danke. (Und viel eleganter als die Lösung, die ich unten gegeben habe.) –

+0

@Sean D: Es macht auch eine schöne Illustration, wo Ihr Mega Hack scheitern würde - überlegen Sie, was passieren würde, wenn es mit einem Operator auf 'Expression Integer' Werten verwendet würde . "Sum 6 2" ist nicht gleich "Literal 8", obwohl mein Code eine vollkommen sinnvolle Instanz von "Num" ist. –

6

Es überschneidet sich, weil (+) einfach als Variable behandelt wird, was bedeutet, dass der Bezeichner + auf der RHS an die Funktion gebunden ist, die Sie "show on" genannt haben.

Es gibt keine Möglichkeit, die Übereinstimmung von Funktionen so zu gestalten, wie Sie möchten.

9

Hier ist eine Möglichkeit, darüber nachzudenken. Überlegen Sie:

answer = 42 
magic = 3 

specialName :: Int -> String 
specialName answer = "the answer to the ultimate question" 
specialName magic = "the magic number" 
specialName x = "just plain ol' " ++ show x 

Können Sie sehen, warum das nicht funktioniert? answer in der Musterübereinstimmung ist eine Variable, die sich von answer im äußeren Bereich unterscheidet. Anstatt also, würden Sie diese wie schreiben:

answer = 42 
magic = 3 

specialName :: Int -> String 
specialName x | x == answer = "the answer to the ultimate question" 
specialName x | x == magic = "the magic number" 
specialName x = "just plain ol' " ++ show x 

In der Tat, das ist genau das, was los ist, wenn Sie Konstanten in einem Muster schreiben. Das heißt:

digitName :: Bool -> String 
digitName 0 = "zero" 
digitName 1 = "one" 
digitName _ = "math is hard" 

durch den Compiler zu etwas Gleichwertiges zu konvertiert wird:

digitName :: Bool -> String 
digitName x | x == 0 = "zero" 
digitName x | x == 1 = "one" 
digitName _ = "math is hard" 

Da Sie wollen gegen die Funktion (+) eher gebunden passen als nur etwas zu dem Symbol binden (+), Sie‘ d müssen Sie den Code schreiben, wie:

instance Show (t -> t-> t) where 
show f | f == (+) = "plus" 
show f | f == (-) = "minus" 

Aber dies würde erfordern, dass Funktionen für eq vergleichbar waren Qualität. Und das ist ein unentscheidbares Problem im Allgemeinen.

Sie könnten zähmen, dass Sie nur das Laufzeitsystem bitten, Funktionszeiger zu vergleichen, aber auf der Sprachebene hat der Haskell-Programmierer keinen Zugriff auf Zeiger. Mit anderen Worten, Sie können Verweise auf Werte in Haskell (*) nicht bearbeiten, sondern nur Werte selbst. Dies ist die Reinheit von Haskell und erhält referentielle Transparenz.

(*) MVar s und andere solche Objekte in der IO Monade sind eine andere Sache, aber ihre Existenz macht den Punkt nicht ungültig.

+0

Es wird alles klar! Ich habe versucht, auch (+) und (-) Instanzen von Eq zu machen, aber das macht offensichtlich (jetzt) ​​auch keinen Sinn. Danke für die tolle Antwort. –

1

es gelöst mich mit einem Mega-Hack.

instance (Num t) => Show (t -> t-> t) where 
show op = 
    case (op 6 2) of 
     8 -> "plus" 
     4 -> "minus" 
     12 -> "times" 
     3 -> "divided"