如何将元素添加到列表,如果它不存在'镜头'?

问题描述:

假设我有一个列表[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包系列。

+0

@chepner非常接近,但不完全。正如你所看到的,我正在使用列表,所以效率并不是我的首要任务。尽管我认为我的解决方案已经足够高效,甚至可能只使用一个遍历列表。我想要更短的形式(没有大的效率损失)。我对使用镜头的解决方案很感兴趣(因为我可以自己做无镜头镜头)。不同的是,在你的表单中'myUpdate'应该检查是否修改元素,而在我的表单中'map myUpdate'会改变列表中的每个元素。但你的想法与我想要的非常接近。 – Shersh

+0

哦,对不起。我想清楚你真正想要什么,然后忘了删除我的评论。 – chepner

你应该能够使用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