您可以从标准或应有的部分组装此功能。接受的答案有关于拉链的正确线索。我的回答about differentiation and comonads给出了相关操作的一般处理,但我在这里具体说一下。
我将“一个元素洞的列表”的类型定义如下:
data Bwd x = B0 | Bwd x :< x deriving Show
type HoleyList x = (Bwd x, [x])
严格来说,我不需要引入反向列表来做到这一点,但如果我必须在脑海中扭转事情,我很容易混淆。 (碰巧HoleyList 是[] 的形式派生词。)
我现在可以在其上下文中定义什么是列表元素。
type InContext x = (HoleyList x, x)
这个想法是该对的第二个组件属于后向列表和前向列表之间。我可以定义将列表重新组合在一起的函数(在通用处理中称为upF。)
plug :: InContext x -> [x]
plug ((B0, xs), y) = y : xs
plug ((xz :< x, xs), y) = plug ((xz, y : xs), x)
我还可以定义提供所有方法来拆分列表的函数(通常是downF)。
selections :: [x] -> [InContext x]
selections = go B0 where
go xz [] = []
go xz (x : xs) = ((xz, xs), x) : go (xz :< x) xs
注意
map snd (selections xs) = xs
map plug (selections xs) = map (const xs) xs
现在我们很高兴遵循 Bartek 的食谱。
selectModify :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
selectModify p f = map (plug . (id *** f)) . filter (p . snd) . selections
即:通过测试过滤选择,将函数应用于焦点元素,然后重新组合在一起。如果你有拉链设备,它是单线的,它应该适用于任何可微的函子,而不仅仅是列表!大功告成!
> selectModify ((1 ==) . (`mod` 2)) (2*) [1..10]
[[2,2,3,4,5,6,7,8,9,10]
,[1,2,6,4,5,6,7,8,9,10]
,[1,2,3,4,10,6,7,8,9,10]
,[1,2,3,4,5,6,14,8,9,10]
,[1,2,3,4,5,6,7,8,18,10]]