2017-09-24 4 views
2

Disons que j'ai une liste [A]. Je veux mettre à jour un élément spécifique de la liste s'il satisfait un prédicat. Mais s'il n'y a pas un tel élément, je veux ajouter un élément à la liste en premier. Ma solution actuelle consiste à écrire manuellement la fonction pour insérer un élément dans la liste si ce n'est pas le cas, puis utiliser filtered Traversal pour mettre à jour mon élément. Comme ceci:Comment ajouter un élément à la liste s'il n'y a pas de 'lentille'?

-- 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 

Je me demande s'il y a une meilleure solution (plus courte, plus idiomatique)? Si possible, j'apprécierais la solution qui utilise seulement la famille de paquets microlens.

+0

@chepner très proche mais pas exactement. Comme vous pouvez le voir, j'utilise des listes, donc l'efficacité n'est pas la priorité absolue pour moi. Bien que je pense que ma solution est déjà assez efficace et utilise probablement même une seule traversée de liste. Je veux une forme plus courte (sans grosse perte d'efficacité). Et je suis intéressé par une solution qui utilise des lentilles (parce que w/o lentilles je peux le faire moi-même). La différence est que dans votre formulaire 'myUpdate' devrait vérifier s'il faut modifier l'élément alors que dans ma forme' map myUpdate' va changer tous les éléments de la liste. Mais ton idée est très proche de ce que je veux. – Shersh

+0

Oh, désolé. J'ai compris ce que vous vouliez vraiment, puis j'ai oublié de supprimer mon commentaire. – chepner

Répondre

0

Vous devriez pouvoir utiliser has pour raccourcir un peu:

functionIWantToWrite :: [A] -> [A] 
functionIWantToWrite = modifyItem . addEmptyItem 
    where 
    _items = filtered myPredicate 
    addEmptyItem list | has _items list = list 
         | otherwise  = item : list 
    modifyItem = each . _items %~ myUpdate 

Si vous vouliez vraiment raccourcir, vous pourriez être en mesure d'obtenir tout comme un seul traversal en utilisant l'Monoid m => Applicative (m,) exemple ou alors, quelque chose comme

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