Variable Daten- und Fkts-namen < Haskell < Programmiersprachen < Praxis < Informatik < Vorhilfe
|
Status: |
(Frage) beantwortet | Datum: | 19:05 Fr 12.01.2007 | Autor: | no_max |
Ich habe diese Frage in keinem Forum auf anderen Internetseiten gestellt.
(<-- soll man ja reinschreiben)
ich habe folgenden (funktionierenden) Code (habe ihn mal reduziert - tatsächlich gibt es noch mehr Funktionen und Datentypen, aber ein Beispiel sollte hier reichen):
data BinTree1 = Leaf1 Float | Node1 BinTree1 BinTree1 deriving Show
data BinTree2 = Leaf2 | Node2 BinTree2 BinTree2 Float deriving Show
preOrder1 :: BinTree1 -> [Float]
preOrder1 (Leaf1 n) = [n]
preOrder1 (Node1 left right) = preOrder1 left ++ preOrder1 right
preOrder2 :: BinTree2 -> [Float]
preOrder2 (Leaf2) = []
preOrder2 (Node2 left right value) = [value] ++ preOrder2 left ++ preOrder2 right
Meine Idee: Kann man diese Funktionen und/oder Datentypen nicht irgendwie zusammenfasssen, d.h. schon in der Funktionsbenennung irgendwie das PatternMatching von Haskell (bzgl. 1 oder 2) nutzen und dann entsprechend BinTree, Node und Leaf mit dem entsprechenden Suffix zu versehen? Dass ich hier noch auf andere Probleme stoßen werde, weil z.B. der Konstruktor Node2 noch einen Float kriegt im Gegensatz zu Node1, ist mir klar (aber hier erstmal nicht die Frage).
(Oder vielleicht ist letzteres auch schon die Begründung dafür, warum soetwas wie das, was mir als Idee vorschwebt gar nicht vonnöten ist, weil halt sowieso Funktionen mit gleichen Parametern auch gleich heißen können und unterschiedlich benannte Funktionen andererseits halt sowieso unterschiedliche Parameter kriegen...?!)
|
|
|
|
Hallo,
die Antwort warum das nicht geht ist ganz einfach: Überlege Dir doch den Typen der resultierenden Funktion? Ist der nun BinTree1 -> [Float] oder BinTree2 -> [Float]? Beides wird Haskell nicht zulassen, da es ein strenges Typsystem besitzt.
Zwei Möglichkeiten bietet Haskell aber dennoch:
1) Du nimmst Either BinTree1 BinTree2 -> [Float]; dann musst Du aber jedes mal auf die Unterklasse prüfen. (Zur Erinnerung: data Either a b = Left a | Right b). Dies hätte allerdings den Vorteil, dass man eine Liste von Bäumen anlegen kann in der Form [Either BinTree1 BinTree2] und dann diese mit solchen Funktionen direkt bearbeiten könnte.
2) Du machst eine Klasse. Kurz gesagt erlauben Klassen in Haskell das überladen von Operatoren/Funktionen. So kannst Du zum Beispiel eine Klasse wie folgt für dein Problem definieren:
class BinTree a where
preOrder :: a -> [Float]
instance BinTree BinTree1 where
preOrder = preOrder1
instance BinTree BinTree2 where
preOrder = preOrder2
Der Unterschied zur Lösung 1 ist der, dass man seine Bäume nicht extra in einen Either-Typen packen muss. Diese Technik erlaubt natürlich auch nicht die Bäume zu mischen. Es erlaubt einem nur mittels preOrder einemal preOrder1 und ein anderes mal preOrder2 zu identifizieren. Man erstellt noch immer explizit einen Baum von einem bestimmten Typen und benutzt darauf preOrder. Das Haskell-Klassen-System erlaubt es allerdings allgemein Funktionen zu generieren, die einen Typen einer bestimmten Klasse verwenden. So kannst Du zum Beispiel eine Funktion sumTree wie folgt machen:
sumTree :: (BinTree a) => a -> Float
sumTree tree = foldl (+) 0 (preOrder a)
Mit freundlichen Grüßen
Matthias Kretschmer
|
|
|
|