Ich habe eine Liste von Datensätzen und benötigen eine Funktion, die die Liste nach einem Datensatz mit einem bestimmten Namen durchsucht und den Wert ändert von diesem Datensatz OR Wenn kein Datensatz übereinstimmt, fügen Sie einen neuen Datensatz der resultierenden Liste hinzu. Hier ist mein Code so weit:Ändern Element der Liste, wenn es gegen eine Bedingung hält oder eine neue hinzufügen, wenn nicht, mit Data.Lens
import Control.Lens
import Control.Applicative ((<$>), pure)
import Data.List (any)
data SomeRec = SomeRec { _name :: String, _val :: Int }
$(makeLenses ''SomeRec)
_find :: (a -> Bool) -> Simple Traversal [a] a
_find _ _ [] = pure []
_find pred f (a:as) = if pred a
then (: as) <$> f a
else (a:) <$> (_find pred f as)
changeOrCreate :: [SomeRec] -> String -> (Int -> Int) -> [SomeRec]
changeOrCreate recs nameToSearch valModifier =
if (any (\r -> r^.name == nameToSearch) recs)
then over (_find (\r -> r^.name == nameToSearch)) (over val valModifier) recs
else recs ++ [SomeRec nameToSearch (valModifier 0)]
Es funktioniert gut, aber ich frage mich, ob es ein direkterer Weg, dies zu schreiben Data.Lens
mit (ohne if
-Konstrukt)? Muss ich auch die _find
Funktion schreiben oder gibt es etwas Äquivalentes in der Bibliothek?
aktualisieren: Hier ist ein Wesentlicher Inhalt der Quelle zu experimentieren ist: https://gist.github.com/SKoschnicke/5795863
Also, ein 'Prism' lässt mich die Liste ändern? Ich denke, dass ich das testen muss, um komplett zu verstehen, werde das morgen tun! –
Funktioniert gut! Aber soweit ich das verstehe, ist 'pos' mit' filtered' kein gültiges 'Traversal' mehr, oder? Daher ist es nicht generell mit anderen Traversalen zusammensetzbar. –
Es ist in der Tat kein gültiges 'Traversal', wenn Sie es verwenden, um das 'name'-Feld zu ändern und somit das Prädikat nicht mehr halten (" Regeln "können brechen, wie in Matvey's Antwort gezeigt), aber ansonsten ist es mit anderen kompostierbar Durchquerungen. – yairchu