如何将元素添加到列表,如果它不存在'镜头'?
问题描述:
假设我有一个列表[A]
。如果满足某个谓词,我想更新列表的特定元素。但是如果没有这样的元素,我想先添加元素列表。我目前的解决方案是手动编写函数,以便将元素插入列表中,如果它不存在,然后使用遍历来更新元素。像这样:如何将元素添加到列表,如果它不存在'镜头'?
-- inserts element to list if there's no a single element
-- which satisfies given predicate
insertIfNot :: (a -> Bool) -> a -> [a] -> [a]
insertIfNot _ e [] = [e]
insertIfNot p e [email protected](x:xs) = if p x then l else x : insertIfNot p e xs
functionIWantToWrite :: [A] -> [A]
functionIWantToWrite = modifyItem . addEmptyItem
where
addEmptyItem = insertIfNot myPredicate item
modifyItem = each.filtered myPredicate %~ myUpdate
我不知道是否有更好的(更短,更习惯的)解决方案?如果可能的话,我会感激解决方案,它只使用microlens
包系列。
答
你应该能够使用has
缩短了一点:
functionIWantToWrite :: [A] -> [A]
functionIWantToWrite = modifyItem . addEmptyItem
where
_items = filtered myPredicate
addEmptyItem list | has _items list = list
| otherwise = item : list
modifyItem = each . _items %~ myUpdate
如果你真的想缩短它,你也许能够只把它作为使用Monoid m => Applicative (m,)
实例中的一个遍历或类似的东西,像
accAny :: (x -> Bool) -> (x -> x) -> x -> (Any, x)
accAny pred fn x = (Any b, if b then fn x else x)
where b = pred x
functionIWantToWrite list = handle . traverse . accAny myPredicate myUpdate $ list
where handle (Any True, list) = list
handle (_, list) = item : list
@chepner非常接近,但不完全。正如你所看到的,我正在使用列表,所以效率并不是我的首要任务。尽管我认为我的解决方案已经足够高效,甚至可能只使用一个遍历列表。我想要更短的形式(没有大的效率损失)。我对使用镜头的解决方案很感兴趣(因为我可以自己做无镜头镜头)。不同的是,在你的表单中'myUpdate'应该检查是否修改元素,而在我的表单中'map myUpdate'会改变列表中的每个元素。但你的想法与我想要的非常接近。 – Shersh
哦,对不起。我想清楚你真正想要什么,然后忘了删除我的评论。 – chepner