我一直在尝试使用镜头和容器并获得一些成功,但我已经达到了理解使用Data.Map的过滤遍历的理解限制 - 我可以更改地图中的单个实例或遍历所有实例但我无法弄清楚如何对某些可识别的分区(即范围内的键)采取行动。
基本上我正在尝试用类似于Gabriel Gonzalez优秀镜片教程的地图做类似的事情[1]
这是我的代码的工作框架,其中包含traverseSome
函数,我不知道如何编写注释掉的函数。任何帮助都感激不尽!
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RankNTypes #-}
import Control.Lens
import Control.Monad.State
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
type CharSet = Set.Set Char
type MapOfSets = Map.Map Int CharSet
data DB = DB { _mos :: MapOfSets } deriving (Show, Eq)
makeLenses ''DB
initDB :: DB
initDB = DB { _mos = Map.fromList (zip [1..5] (repeat Set.empty)) }
add2Map :: Int -> CharSet -> State DB ()
add2Map i cs = mos.ix i %= (Set.union cs)
traverseAll :: Traversal' DB CharSet
traverseAll = mos.traversed
add2MapsAll :: CharSet -> State DB ()
add2MapsAll cs = traverseAll %= (Set.union cs)
-- <problematic part>
{-
traverseSome :: [Int] -> Int -> Traversal' DB MapOfSets
traverseSome ids i = _
add2MapsSome :: [Int] -> CharSet -> State DB ()
add2MapsSome ids cs = mos.(traverseSome ids 2) %= (Set.union cs)
-}
-- </problematic part>
main :: IO ()
main = do
let db = initDB
let bar = Set.fromList ['a'..'g'] :: CharSet
let baz = Set.fromList ['f'..'m'] :: CharSet
let quux = Set.fromList ['n'..'z'] :: CharSet
let db2 = execState (add2Map 5 bar) db
let db3 = execState (add2MapsAll baz) db
-- let db4 = execState (add2MapsSome [1,3] quux) db
print db2
print db3
-- print db4
[1] http://www.haskellforall.com/2013/05/program-imperatively-using-haskell.html
答案 0 :(得分:1)
我假设你的意思
traverseSome :: [Int] -> Traversal' DB CharSet
这是一个更通用的版本
keys :: Ord k => [k] -> IndexedTraversal' k (Map.Map k a) a
keys ks f m = go ks <&> \m' -> foldr (uncurry M.insert) m m'
where
go [] = pure []
go (i:is) = case Map.lookup i m of
Just a -> (:) . (,) i <$> indexed f i a <*> go is
Nothing -> go is
与ordinals
的{{1}}非常相似(我的版本没有重复,所以请确保列表没有重复)。 Data.Vector.Lens
遍历索引列表并在地图中查找它们,随时添加索引。 go
位遍历已编辑元素的列表,并foldr
将它们返回到原始地图。
您可以将自己的内容写成
inserts
如果你确实想要
traverseSome :: [Int] -> IndexedTraversal' Int DB CharSet
traverseSome is = mos . keys is
add2MapsSome :: [Int] -> CharSet -> State DB ()
add2MapsSome is cs = traverseSome is %= Set.union cs
这可以写成(注意你不应该向traverseSome :: [Int] -> Lens' DB MapOfSets
添加新密钥,否则你将违反镜头规定)
Map
可用于编写submap :: Ord k => [k] -> Lens' (Map.Map k a) (Map.Map k a)
submap ks f m = f (Map.fromList as) <&> (<> m)
where as = Maybe.mapMaybe (\i -> (,) i <$> Map.lookup i m) ks
(但效率较低,因为您创建了中间keys
):
Map
编辑:没有中间列表的版本:
keys :: Ord k => [k] -> IndexedTraversal' k (Map k a) a
keys ks = submap ks . itraversed
答案 1 :(得分:1)
地图是instance of TraversableWithIndex
,因此您可以使用traverseSome :: [Int] -> Traversal' DB CharSet
traverseSome ids = mos . itraversed . indices (`Set.member` idSet) where
idSet = Set.fromList ids
来遍历密钥。 indices
可用于缩小键的范围。
itraversed
请注意,此处traversed
与traversed
不同。 itraversed
始终由元素的序号位置编制索引,而For Each dr As DataRow In dt.Rows
' here i want the row number (row_number) of the row being processed
' so that i can update some values in the previous row of datatable dt
' something like
dt(row_number-1)(0) = 50
Next
可以根据数据结构由各种键类型编制索引。