Как правильно управлять гибкими типизированными неизменяемыми структурами данных в Scala? - PullRequest
3 голосов
/ 10 ноября 2009

Прямо сейчас у меня есть такие классы, как:

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. Материал Option добавляет много шума. Я бы хотел, чтобы этого не было, но я также не хочу использовать нули. Это особенно верно, если принять во внимание все вызовы getOrElse, когда я использую структуру. (Могу поспорить, что у языка есть умный способ декларативно автобоксировать их.)
  3. Это затрудняет массовое назначение (что я делаю, потому что у меня есть массив полей), если подкласс смешивает новые поля, например ::

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

не будет компилироваться, потому что discount не определен в суперклассе и поэтому не является переопределением. Я не уверен, есть ли способ сделать это.

Мой главный вопрос:

  1. Есть ли лучший общий способ управления неизменяемыми структурами данных?
  2. Существует ли простой способ скопировать запись и изменить только одно значение без написания стандартного кода?

например. (Псевдокод}:

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

Я слышал, что 2.8 вводит что-то подобное для тематических классов, однако на языке, который поддерживает неизменяемые структуры данных, я был бы удивлен, обнаружив, что до 2.8 это не легче. Я все еще в 2.7.

Ответы [ 5 ]

2 голосов
/ 11 ноября 2009

Похоже, что проблема очень решена в 2.8:

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

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

Объедините это со значениями по умолчанию, и приведенный вами пример можно легко переписать как класс case, который я считаю «правильным» способом реализации неизменяемого типа данных. (Я не уверен, верно ли это для scala, но, исходя из ocaml / haskell, это кажется правильным.)

В 2.7 вам потребуется реализовать множество вспомогательных функций:

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

Тьфу. Они должны действительно поторопиться 2.8 ...

2 голосов
/ 11 ноября 2009

Нет простого способа клонировать экземпляры. FWIW, неизменяемые структуры данных, как правило, глубокие. Например, класс List имеет только двух членов: hd и tl. Список увеличивается за счет объединения членов.

Вы клонируете такие структуры, создавая минимальное количество новых структур данных и восстанавливая как можно большую часть старой структуры данных. Обычно это делается через рекурсии.

Подробнее об этом вы узнаете в книге Чисто функциональные структуры данных . документ , на котором основана книга, находится в свободном доступе.

Вы можете посмотреть вопросы Scala здесь, чтобы увидеть интересные способы обработки данных Option. К сожалению, у меня нет решений для других ваших проблем.

0 голосов
/ 21 августа 2012

Объективы - отличный инструмент для работы с неизменяемыми структурами данных. См этот вопрос .

0 голосов
/ 11 ноября 2009

Ну, как уже говорилось, в текущей (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
}
0 голосов
/ 11 ноября 2009

Использование Option для поля, которое не является обязательным , кажется мне безумным: почему?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...