haskell - Obteniendo mĂșltiples resultados del mapa con "lente"
lenses lens (2)
Teniendo estas importaciones:
> import Control.Lens
Control.Lens> import qualified Data.Map as Map
y un valor de mapa definido de la siguiente manera:
Control.Lens Map> let m = Map.fromList [(''a'', 1), (''c'', 3), (''b'', 2)]
Puedo obtener sus elementos uno por uno así:
Control.Lens Map> view (at ''b'') m
Just 2
Lo que quiero saber es tener un conjunto de claves como esta:
Control.Lens Map> import qualified Data.Set as Set
Control.Lens Map Set> let keys = Set.fromList [''d'', ''c'', ''b'']
cómo construir un captador (supongo), mediante el cual podré obtener un conjunto (o una lista) de elementos coincidentes:
Control.Lens Map Set> view (**???**) m
[3, 2]
Observe que el resultado contiene solo 2 elementos, porque no hay ninguna coincidencia para una clave ''d''
.
Creo que esta es la solución:
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)
Ejemplo:
>>> toListOf (at "A" <> at "B" <> at "C") (M.fromList [("A", 1), ("B", 2)])
[Just 1, Just 2, Nothing]
La idea es que un Traversal
es un monoide. La solución correcta requeriría la introducción de la Traversal
.
Edición: Aquí está la instancia correcta de Monoid
con todos los chanchullos de nuevo tipo:
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"]
Ejemplo:
>>> toListOf myLens myMap
[Just 1,Just 2, Nothing]
Lo siguiente funcionará si solo desea un captador en varios campos.
Primero, debe hacer que Accessor from lens sea una instancia de Monoid (esa instancia está en en HEAD, pero aún no estrenada ya está definido en lens >= 4
, por lo que solo necesita definir la instancia si está trabajando con una versión anterior de la biblioteca).
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
A continuación, puede usar esa instancia para combinar múltiples lentes / recorridos en un solo recorrido:
>>> 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 utiliza la instancia de Monoid para Accessor y la instancia de Monoid para las funciones.