2016-03-18 7 views
0

Ich versuche, die Kinder eines Baumes in Haskell umzukehren. Der Baum sieht wie folgt aus:Haskell: Wie reverse ich die Kinder eines Baumes rekursiv

data Tree v e = Node v [(e,Tree v e)] 

Im Moment habe ich es mit diesen beiden Funktionen zu tun:

rev :: Tree v e -> Tree v e 
rev (Node v []) = Node v [] 
rev (Node v xs) = 
    let xs' = (rev'(snd (unzip xs))) 
    in Node v (zip (fst (unzip xs)) (map rev xs')) 


rev' :: [Tree v e] -> [Tree v e] 
rev' [] = [] 
rev' (x:xs) = (rev' xs) ++ [x] 

Ich bin nicht einmal sicher, ob dies zu 100% funktioniert. Gibt es eine Möglichkeit, es in nur einer Funktion rekursiv zu machen? Ich fühle mich wie mein Weg ist ineffizient. Ich habe versucht, eine Funktion zu schreiben, die wie folgt aussieht:

revRec :: Tree v e -> Tree v e 
revRec (Node v [])  = Node v [] 
revRec (Node v (x:xs)) = Node v (revRec xs ++ [x]) 

obviosuly kann ich nicht nennen revRec mit xs seit xs eine Liste. Ich kann mich nicht um dieses Problem kümmern, obwohl ich das Gefühl habe, dass es nicht so schwer sein könnte.

+1

Erstens haben auch Sie nicht brauchen die 'rev (Knoten v []) = .. 'die allgemeinen Listenfälle decken auch den leeren Listenfall ab. Zweitens scheint die Verwendung von 'unzip' durchaus sinnvoll - die Verwendung von 'fst' und' snd' (auch mehrere Aufrufe von 'unzip') sollte wahrscheinlich eliminiert werden. Sie können es mit primitiver Rekursion schreiben, zB durch. 'rev (Knoten a ts) = Knoten a $ uncurry (\ xy -> zip x (y [])) $ foldr (\ (e, t) (as, bs) -> (e: wie, bs. (rev t :))) ([], id) ts'. Aber ich bezweifle wirklich, dass dies effizienter ist als die "unzip/reverse" Version (und es ist sicherlich hässlicher). – user2407038

Antwort

2

Zeroth, bereinigen Sie Ihren Code. Sie brauchen nicht so viele Parens, und Sie sollten doppelte Berechnungen vermeiden. und snd auf zweimal die gleiche Sache? Meh. Bessere Nutzung Mustervergleich:

rev :: Tree v e -> Tree v e 
rev (Node v xs) 
    = let (es, cs) = unzip xs 
     xs' = rev' cs 
    in Node v $ zip es (map rev xs') 

rev' :: [Tree v e] -> [Tree v e] 
rev' [] = [] 
rev' (x:xs) = rev' xs ++ [x] 

Erste beachten Sie, dass rev' nicht spezifisch für Listen von Bäumen ist, überhaupt nicht! Es ist nur eine ineffiziente Implementierung von der Standard reverse Funktion, die auf jeder Liste funktioniert.

Sekunde, ist das Entpacken/Zippen ein bisschen ungeschickt. Sind Sie sicher, dass dies tatsächlich das Verhalten ist, das Sie wollen: Im Moment kehren Sie nur die Liste der Kinder um, aber die Reihenfolge Elemente bleibt gleich. Wenn nicht, dann sollten Sie besser tun es alle mit einem Rückwärts und eine Karte:

rev :: Tree v e -> Tree v e 
rev (Node v xs) = Node v . reverse $ map (\(e,c) -> (e, rev c)) xs 

oder, noch schöner,

import Control.Arrow 

rev (Node v xs) = Node v . reverse $ map (second rev) xs 
+1

Ein wählerischer Effizienzpunkt: Aus guten Gründen (wirklich, ich verspreche es), nimmt "reverse" nicht an der Listenfusion teil. Daher "umgekehrt". map f' sollte idealerweise von Hand in eine Anwendung von 'mapReverse' (ein Name, den ich erfunden habe, mit dem offensichtlichen Sinn) verschmolzen werden. – dfeuer

+0

Ein wählerischer didaktischer Punkt: Die'Arrow'-Abstraktion ist selbst für mittelschwere Haskeller wie mich einschüchternd. Daher empfehle ich dringend, stattdessen die 'second' von' Data.Bifunctor' zu verwenden. 'Bifunctor' ist, IMO, eine viel anfängerfreundlichere Abstraktion. – dfeuer

+0

Was ist mit der Verwendung der Functor-Instanz für '(,)'? es ist dasselbe wie "zweite", wenn ich mich richtig erinnere. – epsilonhalbe

Verwandte Themen