2013-06-17 8 views
19

dysponowania tymi Import:Getting wiele wyników z mapy z „soczewki”

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

i wartością mapy zdefiniowane następująco:

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

mogę to elementy, jeden po drugim tak:

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

Co chcę wiedzieć, o zestaw kluczy, takich jak ten:

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

jak skonstruować taką getter (chyba), za pomocą którego będę mógł dostać zestaw (lub listy) pasujących elementów:

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

Zauważ, że wynik zawiera tylko 2 elementy, ponieważ nie ma dopasowania do klucza 'd'.

Odpowiedz

21

Następujące będzie działać, jeśli chcesz tylko getter na wielu polach.

Po pierwsze, trzeba dokonać Accessor z obiektywem wystąpienie monoid (że instancja jest w w głowie, ale jeszcze nie wydanyjuż zdefiniowane w lens >= 4, więc trzeba tylko określić instancję jeśli jesteś praca ze starszą wersją biblioteki).

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 

Następnie można użyć tego wystąpienia do łączenia wielu obiektywy/z przechodzenia w jednym przechodzenie:

>>> 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 wykorzystuje instancję monoid dla Accessor i instancji monoid dla funkcji.

+2

Zaakceptuj tę odpowiedź. Jest znacznie lepszy niż mój. –

+0

Doskonale! Czułem, że to musi być proste. Dziękuję Ci! –

5

myślę, że to jest rozwiązanie:

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) 

Przykład:

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

Chodzi o to, że Traversal jest monoid. Prawidłowe rozwiązanie wymagałoby wprowadzenia nowego numeru Traversal.

Edycja: Oto poprawne Monoid wystąpienie ze wszystkimi newtype shenanigans:

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

Przykład:

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

Dzięki! Drugie rozwiązanie jest bardzo bliskie. Czy jest szansa na jakąkolwiek aktualizację 'myLens', która sprawi, że' toListOf myLens myMap' wyda właściwą listę, jak to zrobi 'catMaybes $ toListOf myLens myMap'? –

+0

@NikitaVolkov Myślę, że tak. Daj mi sekundę, aby spróbować uruchomić. –