lens
包提供了处理键值数据结构的机制,适用于需要对键进行
读取操作而不是修改键的操作。因此,它对于编写
nextIntNextChar
(见下文)没有太大帮助,但对于遍历键值对的其他操作可能会有用。
这个功能由
Data.Lens.Indexed
中的索引光学提供。请注意,这与
Data.Lens.At
中的
at
和
ix
操作是不同的。后者允许您通过索引关注键值数据结构的元素,而前者允许您将“索引”与特定的遍历或其他光学相关联,并通过镜头操作传递或组合其他索引。
例如,如果您想要一个镜头操作来对偶数
Int
键索引的
Char
进行
succ
操作,您可以编写:
succEvenChars :: Map Int Char -> Map Int Char
succEvenChars m = m & itraversed.indices even %~ succ
在这里,
itraversed
使用
Int
键作为索引构建了一个
Map
的索引遍历。
indices
光学选择其索引与特定谓词匹配的元素。 生成的索引遍历可以像通常一样与修改操作
%~
一起使用。
(警告:使用
traversed
替代
itraversed
进行类型检查,但会产生不同的答案。
traversed
光学也创建了一个索引遍历,但索引不是来自地图的
Int
键;它是从零开始按遍历顺序计算元素的
Int
索引。 对于此示例,第一个元素具有偶数索引0,但具有奇值键1,应用了
succ
的元素��唯一一个。)
提供了修改
索引的操作,但那只是键的副本,因此修改它不会重新对原始数据结构进行键排序。因此,使用索引遍历实现的
nextIntNextChar
并不是非常令人兴奋。类似于以下内容的东西:
nextIntNextChar :: Map Int Char -> Map Int Char
nextIntNextChar m = fromList (m ^.. reindexed succ itraversed.to succ.withIndex)
这使用`itraversed`构建一个带有键作为索引的索引遍历,将`reindexed succ`应用于此索引遍历以转换索引,使用`to succ`光学来转换值,并使用`withIndex`光学生成键值对。查看(`^..`)结果光学会产生一系列新的键值对,然后可以使用`fromList`将其加载到新的映射中。这是一个`lens`解决方案,没错,但与其他答案和评论中提供的解决方案相比,它在可读性和实用性上并没有明显优势。
请注意,索引光学可以从键中“加载”索引,但索引本身可以保存几乎任何内容,不需要是键,特别是不需要唯一地指定一个值。例如,如果您有来自`Data.Tree`的`Tree`,可以使用`itraversed`使用“默认键”生成一个带索引的遍历。该键是给出树中“路径”的整数索引列表,因此其长度是节点的深度。
使用“重新索引长度”来按深度重新索引遍历,然后可以对同一深度的所有节点执行遍历操作。
import Data.Tree
import Data.Tree.Lens
import Control.Lens
t1 :: Tree Char
t1 = Node 'a' [Node 'b' [Node 'c' [],Node 'd' []], Node 'e' [Node 'f' []], Node 'g' []]
main = do
let nodesAt depth = reindexed length itraversed . index depth
printTree = putStrLn . drawTree . fmap (:[])
print $ t1 ^.. nodesAt 2
printTree $ t1 & nodesAt 1 .~ 'x'
traverse
是一种遍历。每个镜头都是一种遍历,但并非每种遍历都是镜头。遍历可以达到零个或多个目标,而镜头总是只能达到一个目标。如果你想要一个包括遍历、镜头(以及其他东西)的通用术语,你可以使用"视光器"。 - duplodeMap
模块中的mapKeysWith
函数,可以解决你的问题。nextIntNextChar = mapKeysWith succ succ
。 - freestyleMap
模块中的mapKeysWith
函数可以解决你的问题了。nextIntNextChar = mapKeysWith succ succ
。 - undefinednextIntNextChar = fmap succ . mapKeysMonotonic succ
- freestylenextIntNextChar = fmap succ . mapKeysMonotonic succ
。 - undefined