2014-02-28 11 views
23

ist ein Beispiel der Verwendung eines Reißverschlusses in Haskell:Was sind die Unterschiede zwischen Brillengläsern und Reißverschlüssen? Diese

data Tree a = Fork (Tree a) (Tree a) | Leaf a 
data Cxt a = Top | L (Cxt a) (Tree a) | R (Tree a) (Cxt a) 
type Loc a = (Tree a, Cxt a) 

left :: Loc a -> Loc a 
left (Fork l r, c) = (l, L c r) 

right :: Loc a -> Loc a 
right (Fork l r, c) = (r, R l c) 

top :: Tree a -> Loc a 
top t = (t, Top) 

up :: Loc a -> Loc a 
up (t, L c r) = (Fork t r, c) 
up (t, R l c) = (Fork l t, c) 

upmost :: Loc a -> Loc a 
upmost [email protected](t, Top) = l 
upmost l = upmost (up l) 

modify :: Loc a -> (Tree a -> Tree a) -> Loc a 
modify (t, c) f = (f t, c) 

Dies ist ein Beispiel der Verwendung eines Reißverschlusses in Clojure:

(use 'clojure.zip) 
(require '[clojure.zip :as z]) 

user> (def z [[1 2 3] [4 [5 6] 7] [8 9]]) 
#'user/z 

user> (def zp (zipper vector? seq (fn [_ c] c) z)) 
#'user/zp 

user> zp 
[[[1 2 3] [4 [5 6] 7] [8 9]] nil] 

user> (-> zp down) 
[[1 2 3] {:l [], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([4 [5 6] 7] [8 9])}] 

user> (first (-> zp down)) 
[1 2 3] 

Dies ist ein Beispiel für die Verwendung einer Linse in Haskell:

data Person = P { name :: String 
       , addr :: Address 
       } 
data Address = A { street :: String 
       , city :: String 
       , postcode :: String 
       } 

setPostcode :: String -> Person -> Person 
setPostcode pc p = p { addr = addr p { postcode = pc }} 

Dies ist ein Beispiel für die Verwendung eines Objektivs in Clojure.

Nun scheint es, dass sowohl Objektive als auch Zipper funktionale Wege sind, verschachtelte Datenstrukturen zu durchlaufen.

Meine Frage ist: Was sind die Unterschiede zwischen Linsen und Reißverschlüssen? Ist einer für einen bestimmten Anwendungsfall geeignet?

+2

Sie können sich in Reißverschlüssen bewegen? Im Allgemeinen haben Reißverschlüsse "nach links gehen", "nach oben gehen", usw. Primitive; Normalerweise kann man ein Objektiv nicht ein wenig nach links bewegen. Sie sind jedoch eng miteinander verbunden. – drquicksilver

+0

Ein Objektiv ist konzeptuell ein Getter/Setter-Paar, das beliebig tief in eine Datenstruktur eindringen kann (es ist sogar noch etwas allgemeiner). Ein Zipper ist eine bestimmte Art von Datenstruktur, in der Sie (mindestens) nach links und rechts/oben und unten gehen können. –

Antwort

24

Reißverschlüsse sind Cursors ähnlich: sie ermöglichen, Bäume in einer geordneten Weise zu überqueren. Ihre üblichen Operationen sind up, down, left, right und edit. (Namen können je nach Impl variieren)

Objektive sind eine Art generalisierter Schlüssel (wie in "Schlüssel einer assoziativen Datenstruktur"). Die Struktur muss nicht bestellt werden. Ihre üblichen Operationen sind fetch und putback und sind sehr ähnlich zu get und assoc. (Namen können je nach Impl variieren)

Also, wie Sie sehen, sind Reißverschlüsse sehr besorgt über Hierarchie (oben/unten) und Reihenfolge (links/rechts), während Linsen gerade Fokus (daher der Name) auf einem Stück sind Daten, die sogar eine Projektion sein können (das ist etwas, das in der ursprünglichen Struktur nicht alleine existierte).

Zum Beispiel habe ich in meiner laufenden Arbeit an Enliven Linsen, die es mir erlauben, auf ein einzelnes Klasse oder Stil-Attribut in einem HTML-Dokument zu konzentrieren.

7

Zippers sind eine Variante eines Datentyps, der den Typ in seine lokalen Kontext und seine Ausdehnungen in alle Richtungen ausbreitet. Auf einem Zipper können Sie effizient Bewegung und lokale Aktualisierung implementieren.

Objektive sind erstklassige Untersuchungen einer bestimmten Komponente eines Datentyps. Sie konzentrieren sich auf 0, 1 oder viele Unterabschnitte einer Datenstruktur. Bemerkenswerterweise ist Ihr Beispiel für ein Objektiv in Haskell kein Objektiv - es ist nicht erstklassig.

Es ist durchaus sinnvoll, ein Objektiv zu bauen, das auf einen Teil eines Reißverschlusses fokussiert.Zum Beispiel ist eine noch einfachere Reißverschluss als Ihre Beispiele ein Cons Liste Reißverschluss

data Cons a = Empty | Cons a (Cons a) 

data ConsZ a = ConsZ { lefts :: Cons a; here :: a; rights :: Cons a } 

zip :: Cons a -> Maybe (ConsZ a) 
zip Empty = Nothing 
zip (Cons a as) = ConsZ Empty a as 

unzip :: ConsZ a -> Cons a 
unzip (ConsZ Empty a as) = Cons a as 
unzip (ConsZ (Cons l ls) a as) = unzip (ConsZ ls) l (Cons a as) 

Wir können diese Struktur schrittweise ändern, die linke oder rechte

moveRight :: ConsZ a -> Maybe (ConsZ a) 
moveRight (ConsZ _ _ Empty) = Nothing 
moveRight (ConsZ ls x (Cons a as)) = ConsZ (Cons x ls) a as 

und ändern Sie den aktuellen lokalen Punkt

Fokus bewegt
modify :: (a -> a) -> ConsZ a -> ConsZ a 
modify f (ConsZ ls x rs) = ConsZ ls (f x) rs 

Wir können auch Linsen, die jeden Teil des Reißverschlusses Struktur

zugreifen bauen
type Lens s a = forall f . Functor f => (a -> f a) -> (s -> f s) 

_lefts :: Lens (ConsZ a) a 
_lenfs inj (ConsZ ls x rs) = (\ls -> ConsZ ls' x rs) <$> inj ls 

_here :: Lens (ConsZ a) a 
_here inj (ConsZ ls x rs) = (\x' -> ConsZ ls x' rs) <$> inj x 

Und selbst nutzen sie unsere Reißverschluss Aktionen

over :: ((a -> Identity a) -> s -> Identity s) -> (a -> a) -> (s -> s) 
over l f s = runIdentity (l (Identity . f) s) 

modify = over _here 

Letztlich aber effektiv zu bauen, eine Linse ist immer ein First-Class-Zugriff auf einen bestimmten Punkt in einer Datenstruktur. Sie können zusammengesetzt werden, die die Illusion von "Bewegung" in einer Art gibt, aber wenn Sie das wirklich wollen, dann sollten Sie den Reißverschluss verwandeln und einen echten Reißverschlusstyp verwenden.

Verwandte Themen