2013-06-17 28 views
19

Có những hàng nhập khẩu:Bắt nhiều kết quả từ bản đồ với "ống kính"

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

và một giá trị bản đồ được xác định như sau:

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

tôi có thể nhận được đó là yếu tố từng người một như vậy:

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

Điều tôi muốn biết là, có một bộ khóa như sau:

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

làm thế nào để xây dựng như một getter (tôi đoán), sử dụng mà tôi sẽ có thể để có được một bộ (hoặc một danh sách) của phù hợp với các yếu tố:

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

ý rằng kết quả chứa chỉ có 2 phần tử, bởi vì không có kết quả phù hợp cho khóa 'd'.

Trả lời

21

Sau đây sẽ có tác dụng nếu bạn chỉ muốn một getter trên nhiều lĩnh vực.

Trước tiên, bạn cần phải thực hiện accessor từ ống kính một thể hiện của monoid (ví dụ đó là trong trong HEAD, nhưng không phát hành được nêuđã được xác định trong lens >= 4, vì vậy bạn chỉ cần xác định các trường hợp nếu bạn làm việc với một phiên bản cũ của thư viện).

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 

Sau đó bạn có thể sử dụng ví dụ đó để kết hợp nhiều thấu kính/traversals thành một traversal duy nhất:

>>> 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 sử dụng Ví dụ monoid cho accessor và dụ monoid cho các chức năng.

+2

Chấp nhận câu trả lời này. Nó tốt hơn tôi nhiều. –

+0

Tuyệt vời! Tôi cảm thấy nó phải đơn giản. Cảm ơn bạn! –

5

Tôi nghĩ rằng đây là giải pháp:

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) 

Ví dụ:

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

Ý tưởng là một Traversal là một monoid. Giải pháp chính xác sẽ yêu cầu nhập số Traversal.

Edit: Đây là đúng Monoid dụ với tất cả các shenanigans 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"] 

Ví dụ:

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

Cảm ơn! Giải pháp thứ hai rất gần. Có cơ hội của bất kỳ cập nhật nào cho 'myLens', mà sẽ làm cho' toListOf myLens myMap' phát ra một danh sách thích hợp như trong 'catMaybes $ toListOf myLens myMap' hiện tại sẽ làm gì? –

+0

@NikitaVolkov Tôi nghĩ vậy. Hãy cho tôi một giây để cố gắng làm cho nó hoạt động. –

Các vấn đề liên quan