วิธีชวเลขในการกำหนดเขตข้อมูลเดียวในบันทึกในขณะที่คัดลอกเขตข้อมูลที่เหลือ?


119

สมมติว่าฉันมี ADT บันทึกต่อไปนี้:

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

ฉันต้องการฟังก์ชันที่ใช้บันทึกและส่งคืนเร็กคอร์ด (ประเภทเดียวกัน) โดยที่ฟิลด์ทั้งหมดยกเว้นฟิลด์ใดฟิลด์หนึ่งมีค่าที่เหมือนกันกับฟิลด์ที่ส่งผ่านเป็นอาร์กิวเมนต์ดังนี้:

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

ข้างต้นใช้งานได้ แต่สำหรับบันทึกที่มีฟิลด์มากกว่า (พูด10) การสร้างฟังก์ชันดังกล่าวจะทำให้เกิดการพิมพ์จำนวนมากซึ่งฉันรู้สึกว่าไม่จำเป็นเลยทีเดียว

มีวิธีที่น่าเบื่อน้อยกว่าในการทำเช่นเดียวกันหรือไม่?


3
มีการบันทึกไวยากรณ์สำหรับการอัปเดต แต่จะยุ่งยากอย่างรวดเร็ว ลองดูที่เลนส์แทน
Cat Plus Plus

คำตอบ:


155

ใช่มีวิธีที่ดีในการอัปเดตฟิลด์บันทึก ใน GHCi คุณสามารถทำได้ -

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

9
RecordWildCardsขยายสามารถที่ดีเช่นกันกับเขต“แกะ” ในขอบเขต สำหรับการอัปเดตมันไม่ค่อยดีเท่าไหร่:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy

2
BTW ใน Frege (Haskell สำหรับ JVM) คุณจะกำหนดฟังก์ชันเป็นupdateFoo x = x.{ c = "Goodbye" }(สังเกตตัว.ดำเนินการ)
0dB

วิดีโอที่ดีโดยทางyoutube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero

ขอบคุณ เป็นเวลานานแล้วที่ฉันเขียน Haskell!
Chris Taylor

37

นี่เป็นงานที่ดีสำหรับเลนส์ :

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

test = Foo 1 2 "Hello"

แล้ว:

setL c "Goodbye" test

จะอัปเดตฟิลด์ 'c' ของ 'test' เป็นสตริงของคุณ


5
และแพคเกจที่เหมือนเลนส์มักกำหนดตัวดำเนินการนอกเหนือจากฟังก์ชันในการรับและตั้งค่าฟิลด์ ยกตัวอย่างเช่นtest $ c .~ "Goodbye"เป็นวิธีการที่lensจะทำ iirc ฉันไม่ได้พูดนี้เป็น intutitive $แต่เมื่อคุณรู้ว่าผู้ประกอบการแล้วผมคาดหวังว่ามันจะมาได้อย่างง่ายดายเช่น
Thomas M. DuBuisson

3
คุณรู้หรือไม่ว่าsetLหายไปไหน? ฉันกำลังนำเข้าControl.Lensแต่ ghc รายงานว่าsetLไม่ได้กำหนด
dbanas

1
ใช้ set แทน setL
Subhod I

16

คุณไม่จำเป็นต้องกำหนดฟังก์ชันเสริมหรือใช้เลนส์ Standard Haskell มีสิ่งที่คุณต้องการอยู่แล้ว ลองดูตัวอย่างโดย Don Stewart:

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

test = Foo 1 2 "Hello"

จากนั้นคุณสามารถพูดtest { c = "Goodbye" }เพื่อรับบันทึกที่อัปเดต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.