在Scala中管理灵活,类型化,不可变数据结构的正确方法是什么?

时间:2009-11-10 20:05:09

标签: scala functional-programming

现在我有类似的课程:

abstract class Record {
  // Required fields
  val productCode:Option[String]
  val price:Option[Double]

  // Optional fields
  val notes:Option[String] = None
  val used:Option[Boolean] = Option(false)
}

然后创建它们:

val r = new Record {
  override val productCode = Option("abc")
  override val price = Option(32.12)
}

有几点需要注意:

  1. 我使用Option作为非可选字段,以便 一个。我不必记住哪些字段是可选的 湾我可以在不更改界面的情况下更改哪些字段是可选的
  2. 选项内容会增加很多噪音。我不喜欢那里,但我也不想使用空值。当我在使用结构时考虑到对getOrElse的所有调用时尤其如此。 (我敢打赌,这种语言有一种聪明的方式可以通过声明的形式自动进行这些操作。)
  3. 如果子类混合了新的字段,这使得质量分配(我正在做,因为我有一个字段数组)很难,例如:

    override val List(productCode, price, discount) = fields // fields is a List

  4. 将无法编译,因为超类中未定义discount,因此没有覆盖。我不确定是否有办法做到这一点。

    我的主要问题是:

    1. 管理不可变数据结构有更好的整体方法吗?
    2. 是否有直接的方法来复制记录并只更改一个值而无需编写样板代码?
    3. e.g。 (伪代码}:

      val r2 = r.clone { override val used = true }
      

      我听说2.8为案例类引入了类似的东西,但是在一种鼓励不可变数据结构的语言中,我会惊讶地发现在2.8之前这并不容易。我还在2.7。

5 个答案:

答案 0 :(得分:2)

克隆实例没有简单的方法。 FWIW,不可变数据结构通常很深。例如,List类只有两个成员:hdtl。通过链接成员来增加列表。

您通过创建最少量的新数据结构并尽可能多地重新引用旧数据结构来克隆此类结构。通常,这是通过完成的 递归。

您可以在Purely Functional Data Structures一书中了解更多相关信息。该书所依据的paper免费提供。

您可以在此处查找Scala问题,以查看处理Option数据的有趣方法。不幸的是,我对你的其他问题没有任何解决方案。

答案 1 :(得分:2)

这看起来是2.8中非常重要的问题:

case class Employee(name: String, age: Int)

val joe = Employee("Joe", 25)
val bob = joe copy (name = "Bob")

将此与默认值相结合,您给出的示例可以轻松地重写为案例类,我认为这是实现不可变数据类型的“正确”方法。 (我不确定scala是否属实,但是来自ocaml / haskell,似乎是正确的。)

在2.7中你将不得不实现许多辅助函数:

def asUsed(r: Record): Record = {
  Record(r.productCode, r.price, r.nodes, Some(true))
}

呸。他们应该真的快点2.8 ......

答案 2 :(得分:0)

使用Option 一个不可选的字段对我来说似乎很疯狂:为什么?

答案 3 :(得分:0)

正如已经说过的那样,在当前(2.7)Scala中没有直接的方法可以做到这一点,但从我的角度来看,使用构建器模式可以很容易地完成它。代码演示:

abstract class Record {
  // Required fields
  val productCode:Option[String]
  val price:Option[Double]

  // Optional fields
  val notes:Option[String] = None
  val used:Option[Boolean] = Option(false)
}
class RecordBuilder{
  private var _productCode:Option[String] = null
  private var _price:Option[Double] = null

  // Optional fields
  private var _notes:Option[String] = None
  private var _used:Option[Boolean] = Option(false)

  def productCode(in:Option[String]) : RecordBuilder = {
    _productCode = in
    this
  }
  def price(in : Option[Double]) : RecordBuilder = {
    _price = in
    this
  }
  def notes(in:Option[String]) : RecordBuilder = {
    _notes = in
    this
  }
  def used (in : Option[Boolean]) : RecordBuilder = {
    _used = in
    this
  }

  def create() : Record  =  {
   val r =  new Record = {
      override productCode = _productCode
      override price = _price
      override notes = _notes
      override used  = _used

    }
  r
}
}
object Record{
  def from(in:Record) : RecordBuilder = {
    val r = new RecordBuilder
    r.productCode(in.productCode).price(in.price).notes(in.notes)
    .used(in.used)
  }
}
object Test {
  def main(args:Array[String]) = {
    val r = new Record {
    override val productCode = Option("abc")
    override val price = Option(32.12)}
  }
  val r1 = Record.from(r).used(true).create
}

答案 4 :(得分:0)

镜头是操作不可变数据结构的绝佳工具。请参阅this question