haskell lenses lens

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.