3

我的代码中有一个通用模式,其中有一个对象列表,我将它们传递给一个函数choose让用户选择一个。

choose :: [a] -> (a -> String) -> IO Int

但是,返回 anInt是不幸的,因为现在我必须使用部分(!!)来访问所选元素。为避免这种情况,我宁愿拥有

choose :: [a] -> [Lens' [a] a] -> (a -> String) -> IO (Lens' [a] a)

我传入一个镜头列表,其中一个用于访问列表中的每个元素。然后,我可以确定使用修改镜头另一端的对象是安全的,而不用担心它是否真的存在。

我怎么能创建这样一个镜头列表?实际上,我需要

makeAccessors :: [a] -> [Lens' [a] a]

作为奖励,更一般的东西

makeAccessors' :: a -> Traversal' a b -> [Lens' a b]

也会很棒 - 为遍历访问的每个点制作一个镜头

4

1 回答 1

1

也许不是镜头,而是拉链

data ListZipper a = LZ { left :: [a], focus :: a, right :: [a] }

listToListZipper :: [a] -> Maybe (ListZipper a)
listToListZipper (a:as) = Just $ LZ [] a as
listToListZipper []     = Nothing

modifyFocus :: (a -> a) -> ListZipper a -> ListZipper a
modifyFocus f z = z { focus = f $ focus z }

goRight :: ListZipper a -> Maybe (ListZipper a)
goRight (LZ ls l (a:rs)) = Just $ LZ (l:ls) a rs
goRight _                = Nothing

goLeft :: ListZipper a -> Maybe (ListZipper a)
goLeft (LZ (a:ls) r rs) = Just $ LZ ls a (r:rs)
goLeft _                = Nothing

listZipperToList :: ListZipper a -> [a]
listZipperToList (LZ ls a rs) = reverse ls ++ a:rs

访问所需的元素很容易(使用modifyFocus),您可以使您的choose

choose :: [a] -> (a -> String) -> MaybeT IO (ListZipper a)
choose as f = do 
    i <- lift $  choose' as f
    let z = listToListZipper as
    MaybeT . return $ goto i z
  where choose' :: [a] -> (a -> String) -> IO Int
        choose' = ...
        goto :: Int -> ListZipper a -> Maybe (ListZipper a)
        goto 0 z = return z
        goto n z = do
          z' <- goRight z
          goto (n-1) z'

甚至

forceValidChoice :: [a] -> (a -> String) -> IO (ListZipper a)
forceValidChoice as f = do
  mz <- runMaybeT $ choose as f
  maybe (forceValidChoice as f) return mz
于 2013-07-15T03:32:12.643 回答