2013-06-17 12 views
19

Avere queste importazioni:ottenere più risultati dalla mappa con la "lente"

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

e un valore mappa definito come segue:

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

posso ottenere che sia gli elementi uno per uno in questo modo:

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

Quello che voglio sapere è, avendo un set di chiavi di questo tipo:

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

come costruire un tale getter (credo), con il quale sarò in grado di ottenere un insieme (o una lista) di elementi corrispondenti:

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

Si noti che il risultato contiene solo 2 elementi, perché non c'è corrispondenza per una chiave 'd'.

risposta

21

Di seguito funzionerà se desideri solo un getter su più campi.

In primo luogo, è necessario effettuare Accessor dall'obiettivo un'istanza di Monoide (che istanza è in nella testa, ma non ancora rilasciatogià definito in lens >= 4, in modo che solo bisogno di definire l'istanza, se siete lavorando con una vecchia versione della libreria).

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 

è quindi possibile utilizzare tale istanza per combinare più lenti/attraversamenti in un unico attraversamento:

>>> 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 utilizza l'istanza Monoide per d'accesso e l'istanza Monoide per le funzioni.

+2

Accetta questa risposta. È molto meglio del mio –

+0

Eccellente! Ho sentito che doveva essere semplice. Grazie! –

5

penso che questa è la soluzione:

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) 

Esempio:

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

L'idea è che un Traversal è un monoide. La soluzione corretta richiederebbe il newtyping dello Traversal.

Edit: Ecco la corretta Monoid un'istanza con tutti gli imbrogli newtype:

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"] 

Esempio:

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

Grazie! La seconda soluzione è molto vicina. C'è una possibilità di aggiornamento a 'myLens', che renderà' toListOf myLens myMap' emettere un elenco corretto come in che cosa farà attualmente catMaybes $ toListOf myLens myMap'? –

+0

@NikitaVolkov Penso di si. Dammi un secondo per cercare di farlo funzionare. –

Problemi correlati