2013-06-09 6 views
12

Ich habe einige Datentypen entlang der Linie derWie kann ich mit Control.Lens das i-te Element einer Liste aktualisieren?

data Outer = Outer { _list :: [ Inner ] } 
data Inner = Inner { _bool :: Bool } 

Control.Lens verwendet wird, kann ich die _Bool des i-ten Inner Zugang (innen eine Monade 'Staat Outer') wie dieses

boolValue <- gets (^. list . to (!! i) . inner) 

I möchte auch wie diesen Wert mit etwas zu aktualisieren, in der Lage

list ^. (to (!! i)) ^. inner %= True 

jedoch (nach meinem Verständnis), die ‚in‘ Funktion erzeugt nur einen Getter, kein echter len s, das entweder als Getter oder Setter verwendet werden kann.

Also, wie kann ich (!! i) in ein Objektiv konvertieren, mit dem ich dieses Feld aktualisieren kann?

Antwort

15

Sie nicht * können (!!) in jede linsenartige Sache anders als ein Getter drehen - aber es gibt eine Funktion, um diese Art der Sache zu tun: ix, für den Zugriff auf th in Indizes. Es ist eigentlich ein Traversal, kein Lens - was hier bedeutet, dass es nur fehlschlagen kann (wenn der Index außerhalb der Grenzen ist) - aber solange der Index in der Liste ist, wird es funktionieren.

Es gibt ein anderes Problem, obwohl - (^.) ist auch ein Operator, der ausschließlich zum Abrufen von Werten verwendet wird. Es ist inkompatibel mit z.B. (%=), die ein Objektiv-ähnliches Ding als sein erstes Argument nimmt. Und: (%=) dient zum Zuordnen einer Funktion über den vorhandenen Wert; Wenn Sie nur festlegen möchten, können Sie (.=) verwenden. So möchten Sie wahrscheinlich so etwas wie:

list . ix i . inner .= True 

* Es gibt tatsächlich eine Funktion, die dies tun können - es ist upon genannt - aber es nutzt wunderbar böse schwarze Magie und Sie sollten es nicht verwenden, zumindest nicht für dies (und wahrscheinlich nicht für irgendeinen echten Code).

+0

könnten Sie klären, was der Unterschied zwischen 'ix' und' element' ist? Ich bin mit Gabriels "Element" gegangen, da es einfacher scheint, einen "Int" anstelle eines "Index" zu nehmen. Aus den Dokumenten, die Sie verlinkt haben, scheint 'ix' allgemeiner zu sein, was es erlaubt - ist es strikt allgemein? – ajp

+0

Ich dachte nicht an "Element". Es ist nicht genau, dass einer allgemeiner ist als der andere ... 'ix' ist eine Typklasse, die eine Reihe von Instanzen zur Indizierung mit bestimmten Indextypen hat (zum Beispiel Listen mit' Int', 'Map's mit ihr Indextyp funktioniert mit ihrer Domäne). 'element' nimmt einen' Traversable' Typ und zählt von links nach rechts mit einem 'Int' Index. In diesem Fall passieren sie zufällig. – shachaf

+0

Ich habe es nicht getestet, aber ich würde annehmen, dass 'ix' in diesem speziellen Fall ein wenig effizienter ist (hauptsächlich, weil ich keinen Weg gefunden habe,' Indizierung' zu einem guten Code zu machen ... :-(Jemand anderes ist willkommen zu versuchen.) Es ist auch eine etwas ad-hoc-Klasse. – shachaf

8

Verwendung element, die eine Traversal auf das angegebene Listenelement ist:

list . element i . inner %= True :: Outer -> Outer 

Wenn Sie das Listenelement erhalten möchten Sie dies tun müssen Maybe verwenden, da das Listenelement nicht da sein könnte:

myList :: Outer 

myList ^? list . element i . inner :: Maybe Bool 
+0

Beachten Sie, dass '% =' Ihnen eine 'State'-Aktion zum Ändern eines Status gibt, d. H. '... :: State Outer()'. (Ich nehme auch an, die Frage bedeutete '. =' Hier.) – shachaf

+0

Ja, ich meine '% ~', aber ich denke deine Antwort ist sowieso besser. –