2

レンズとコンテナを一緒に使用していくつかの成功を収めようとしましたが、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

4

2 に答える 2

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これはfromと非常によく似ています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

編集: 中間リストのないバージョン:

keys :: Ord k => [k] -> IndexedTraversal' k (Map.Map k a) a
keys ks f m = go ks
  where
    go []     = pure m
    go (i:is) =
      case Map.lookup i m of
        Just a  -> Map.insert i <$> indexed f i a <*> go is
        Nothing -> go is
于 2015-04-28T17:51:06.530 に答える
1

Map は のインスタンスでTraversableWithIndexあるためitraversed、キーのトラバースにも使用できます。indicesキーの範囲を絞り込むために使用できます。

traverseSome :: [Int] -> Traversal' DB CharSet
traverseSome ids = mos . itraversed . indices (`Set.member` idSet) where
  idSet = Set.fromList ids

itraversedこちらとは異なりますのでご注意くださいtraversedtraversed要素の順序位置によって常にインデックスがitraversed作成されますが、データ構造に応じてさまざまなキー タイプによってインデックスが作成される場合があります。

于 2015-04-28T18:38:23.400 に答える