Kurzform zum Zuweisen eines einzelnen Felds in einem Datensatz, während die restlichen Felder kopiert werden?

118

Angenommen, ich habe den folgenden Datensatz ADT:

data Foo = Bar { a :: Integer, b :: String, c :: String }

Ich möchte eine Funktion, die einen Datensatz aufnimmt und einen Datensatz (desselben Typs) zurückgibt, bei dem alle Felder bis auf eines identische Werte wie das als Argument übergebene haben:

walkDuck x = Bar { a = a x, b = b x, c = lemonadeStand (a x) (b x) }

Das Obige funktioniert, aber für einen Datensatz mit mehr Feldern (z. B. 10) würde das Erstellen einer solchen Funktion viel Tippen erfordern, was ich für ziemlich unnötig halte.

Gibt es weniger langwierige Möglichkeiten, dasselbe zu tun?

jaymmer - Monica wieder einsetzen
quelle
3
Die Datensatzsyntax für die Aktualisierung ist vorhanden, wird jedoch schnell umständlich. Schauen Sie sich stattdessen die Objektive an.
Cat Plus Plus

Antworten:

154

Ja, es gibt eine gute Möglichkeit, Datensatzfelder zu aktualisieren. In GHCi können Sie tun -

> data Foo = Foo { a :: Int, b :: Int, c :: String }  -- define a Foo
> let foo = Foo { a = 1, b = 2, c = "Hello" }         -- create a Foo
> let updateFoo x = x { c = "Goodbye" }               -- function to update Foos
> updateFoo foo                                       -- update the Foo
Foo {a = 1, b = 2, c = "Goodbye" }
Chris Taylor
quelle
9
Die RecordWildCardsErweiterung kann auch nützlich sein, um Felder in einem Bereich zu „entpacken“. Für Updates ist es allerdings nicht ganz so schön:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy
2
Übrigens würden Sie in Frege (ein Haskell für die JVM) die Funktion als definieren updateFoo x = x.{ c = "Goodbye" }(beachten Sie den .Operator).
0
Nettes Video übrigens youtube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero
Vielen Dank. Leider ist es lange her, dass ich Haskell geschrieben habe!
Chris Taylor
37

Dies ist ein guter Job für Objektive :

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

Dann:

setL c "Goodbye" test

würde das Feld 'c' von 'test' an Ihrer Zeichenfolge aktualisieren.

Don Stewart
quelle
5
Und linsenähnliche Pakete definieren häufig Operatoren zusätzlich zu Funktionen zum Abrufen und Festlegen von Feldern. Zum Beispiel test $ c .~ "Goodbye"ist, wie lenswürde es iirc tun. Ich sage nicht, dass dies intuitiv ist, aber wenn Sie die Operatoren kennen, dann gehe ich davon aus, dass es so einfach wie möglich sein wird $.
Thomas M. DuBuisson
3
Wissen Sie, wohin setL gegangen ist? Ich importiere Control.Lens , aber ghc meldet, dass setL undefiniert ist.
Dbanas
1
benutze set anstelle von setL
Subhod I
16

Sie müssen keine Hilfsfunktionen definieren oder Objektive verwenden. Standard Haskell hat bereits das, was Sie brauchen. Nehmen wir das Beispiel von Don Stewart:

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

Dann können Sie einfach sagen test { c = "Goodbye" }, um einen aktualisierten Datensatz zu erhalten.

Wolfgang Jeltsch
quelle