2013-06-17 10 views
19

Nachdem diese Einfuhren Anfahrt:mehr Ergebnisse aus der Karte mit „Linse“

> import Control.Lens 
Control.Lens> import qualified Data.Map as Map 

und einem Kartenwert wie folgt definiert:

Control.Lens Map> let m = Map.fromList [('a', 1), ('c', 3), ('b', 2)] 

ich bekommen kann es Elemente ein, wie so von einem ist:

Control.Lens Map> view (at 'b') m 
Just 2 

Was ich wissen will, ist, eine Reihe von Tasten, wie dies mit:

Control.Lens Map> import qualified Data.Set as Set 
Control.Lens Map Set> let keys = Set.fromList ['d', 'c', 'b'] 

wie so zu konstruieren, einen Getter (glaube ich), mit denen ich in der Lage sein, einen Satz (oder eine Liste) von passenden Elementen zu erhalten:

Control.Lens Map Set> view (**???**) m 
[3, 2] 

Beachten Sie, dass das Ergebnis enthält nur 2 Elemente, weil es keine Übereinstimmung für einen Schlüssel 'd' gibt.

Antwort

21

Im Folgenden wird funktionieren, wenn Sie nur einen Getter über mehrere Felder möchten.

Zuerst müssen Sie Accessor von der Linse eine Instanz von Monoid (das Beispiel ist in in HEAD machen, aber noch nicht veröffentlichtbereits in lens >= 4 definiert, so dass Sie nur die Instanz definieren müssen, wenn Sie Arbeiten mit einer alten Version der Bibliothek).

import Data.Monoid 
import Control.Lens 

instance Monoid r => Monoid (Accessor r a) where 
    mempty = Accessor mempty 
    mappend (Accessor a) (Accessor b) = Accessor $ a <> b 

Sie können dann diese Instanz verwenden, um mehrere Linsen/Querungen in einer einzigen Traversal zu kombinieren:

>>> import qualified Data.Set as S 
>>> import qualified Data.Map as M 
>>> import Data.Foldable (foldMap) 
>>> import Control.Lens 
>>> let m = M.fromList [('a',1), ('b',2), ('c',3)] 
>>> let k = S.fromList ['b','c','e'] 
>>> m ^.. foldMap at k 
[Just 2,Just 3,Nothing] 
>>> m ^.. foldMap ix k 
[2,3] 

foldMap verwendet die Monoid-Instanz für Accessor und der Monoid-Instanz für Funktionen.

+2

Akzeptieren Sie diese Antwort. Es ist viel besser als meins. –

+0

Ausgezeichnet! Ich fühlte, dass es einfach sein musste. Vielen Dank! –

5

Ich denke, das ist die Lösung:

import Control.Applicative 
import Control.Lens 
import qualified Data.Map as M 
import Data.Monoid hiding ((<>)) 

empty :: (Applicative f, Monoid a) => (b -> f b) -> (a -> f a) 
empty _ _ = pure mempty 

(<>) 
    :: (Applicative f, Monoid a) 
    => ((b -> f b) -> (a -> f a)) 
    -> ((b -> f b) -> (a -> f a)) 
    -> ((b -> f b) -> (a -> f a)) 
(l1 <> l2) f a = mappend <$> (l1 f a) <*> (l2 f a) 

Beispiel:

>>> toListOf (at "A" <> at "B" <> at "C") (M.fromList [("A", 1), ("B", 2)]) 
[Just 1, Just 2, Nothing] 

Die Idee ist, dass ein Traversal ein Monoid ist. Die richtige Lösung würde eine Neueingabe des Traversal erfordern.

Edit: Hier ist die korrekte Monoid-Instanz mit allen newtype Spielereien:

import Control.Applicative 
import Control.Lens 
import qualified Data.Map as M 
import Data.Monoid 
import Data.Foldable 

newtype Combinable f a b = Combinable { useAll :: (b -> f b) -> (a -> f a) } 

instance (Applicative f, Monoid a) => Monoid (Combinable f a b) where 
    mempty = Combinable (\_ _ -> pure mempty) 
    mappend (Combinable l1) (Combinable l2) 
     = Combinable (\f a -> mappend <$> (l1 f a) <*> (l2 f a)) 

myMap :: M.Map String Int 
myMap = M.fromList [("A", 1), ("B", 2)] 

myLens :: Traversal' (M.Map String Int) (Maybe Int) 
myLens = useAll $ foldMap (Combinable . at) ["A", "B", "C"] 

Beispiel:

>>> toListOf myLens myMap 
[Just 1,Just 2, Nothing] 
+0

Danke! Die zweite Lösung ist sehr nah. Gibt es eine Chance auf ein Update auf 'myLens', was' toListOf myLens myMap' dazu bringt, eine richtige Liste zu erstellen, wie es in 'catMaybes $ toListOf myLens myMap' momentan möglich ist? –

+0

@NikitaVolkov Ich denke schon. Gib mir eine Sekunde, um zu versuchen, es zur Arbeit zu bringen. –