【问题标题】:Shorthand way for assigning a single field in a record, while copying the rest of the fields?在复制其余字段时分配记录中的单个字段的简写方式?
【发布时间】:2013-02-04 00:35:06
【问题描述】:

假设我有以下记录 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),创建这样的函数将需要大量输入,我认为这是完全没有必要的。

有没有更简单的方法来做同样的事情?

【问题讨论】:

  • 存在用于更新的记录语法,但很快就会变得很麻烦。改为查看lenses

标签: haskell record


【解决方案1】:

是的,有一种更新记录字段的好方法。在 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" }

【讨论】:

  • RecordWildCards 扩展也可以很好地“解包”范围内的字段。对于更新,它不是那么好:incrementA x@Foo{..} = x { a = succ a }
  • 顺便说一句,在 Frege(JVM 的 Haskell)中,您可以将函数定义为 updateFoo x = x.{ c = "Goodbye" }(注意 . 运算符)。
  • 顺便说一句不错的视频youtube.com/watch?v=YScIPA8RbVE
  • 谢谢。可悲的是,我已经很久没有写任何 Haskell 了!
【解决方案2】:

这是lenses的好工作:

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

test = Foo 1 2 "Hello"

然后:

setL c "Goodbye" test

会将 'test' 的字段 'c' 更新为您的字符串。

【讨论】:

  • 和 lens-like 包除了获取和设置字段的功能外,还经常定义操作符。例如,test $ c .~ "Goodbye"lens 将如何做 iirc。我并不是说这很直观,但是一旦您了解了运算符,我希望它会像 $ 一样容易。
  • 你知道setL去哪儿了吗?我正在导入 Control.Lens,但 ghc 报告 setL 未定义。
  • 使用 set 代替 setL
【解决方案3】:

您无需定义辅助功能或使用镜头。标准 Haskell 已经拥有你所需要的。让我们以 Don Stewart 为例:

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

test = Foo 1 2 "Hello"

然后你可以直接说test { c = "Goodbye" } 来获取更新的记录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    相关资源
    最近更新 更多