【问题标题】:scala programming without vars没有变量的scala编程
【发布时间】:2012-11-30 22:25:10
【问题描述】:

scala 中的 val 和 var,我认为这个概念很好理解。

我想做这样的事情(类似java):

trait PersonInfo {
  var name: Option[String] = None
  var address: Option[String] = None
  // plus another 30 var, for example 
 }
case class Person() extends PersonInfo
object TestObject {
  def main(args: Array[String]): Unit = {
         val p = new Person()
         p.name = Some("someName")
         p.address = Some("someAddress")
  }
}

所以我可以更改姓名、地址等...

这很好用,但问题是,在我的程序中,我最终将所有内容都作为 vars。 据我了解 val 在scala中是“首选”。 val 如何在此工作 每次更改其中一个参数时都不必重写所有 30 多个参数的示例类型?

也就是说,我可以拥有

trait PersonInfo {
  val name: Option[String] 
  val address: Option[String] 
  // plus another 30 val, for example 
 }
case class Person(name: Option[String]=None, address: Option[String]=None, ...plus another 30.. ) extends PersonInfo
object TestObject {
  def main(args: Array[String]): Unit = {
         val p = new Person("someName", "someAddress", .....)
         // and if I want to change one thing, the address for example
         val p2 = new Person("someName", "someOtherAddress", .....)
  }
}

这是“正常”的 scala 做事方式(不受 22 个参数的限制)吗? 可以看出,我对这一切都很陌生。


一开始是 Tony K. 的基本选项:

def withName(n : String) = Person(n, address)

看起来很有希望,但我有很多扩展 PersonInfo 的类。 这意味着在每一个中我都必须重新实现 defs,大量的打字、剪切和粘贴, 只是做一些简单的事情。 如果我将特征 PersonInfo 转换为普通类并将所有定义放入其中,那么 我有一个问题,我怎样才能返回一个人,而不是一个人信息? 是否有一个聪明的 scala 东西可以以某种方式在 trait 或 super 类中实现并拥有 所有子类真的扩展了吗?

据我所见,当示例非常简单时,所有在 scala 中的工作都非常好, 2 或 3 个参数,但当你有几十个时,它变得非常乏味和不可行。

奇怪加拿大的PersonContext 是我觉得类似的,还在想这个。我猜如果 我有 43 个参数,我需要分解成多个临时类来抽水 将参数放入 Person 中。

复制选项也很有趣,很神秘,但打字少了很多。

来自 java 我希望从 scala 中获得一些聪明的技巧。

【问题讨论】:

    标签: scala


    【解决方案1】:

    案例类有一个预定义的 copy 方法,您应该使用它。

    case class Person(name: String, age: Int)
    
    val mike = Person("Mike", 42)
    
    val newMike = mike.copy(age = 43)
    

    这是如何工作的? copy 只是编译器为您编写的方法之一(除了equalshashCode 等)。在这个例子中是:

    def copy(name: String = name, age: Int = age): Person = new Person(name, age)
    

    此方法中的值nameage 会遮蔽外部范围中的值。如您所见,提供了默认值,因此您只需指定要更改的值。其他默认为当前实例中的内容。

    【讨论】:

    【解决方案2】:

    scala中存在var的原因是为了支持可变状态。在某些情况下,可变状态确实是您想要的(例如,出于性能或清晰度原因)。

    不过,您是对的,鼓励使用不可变状态的背后有很多证据和经验。事情在许多方面(并发性、清晰的原因等)都做得更好。

    您的问题的一个答案是为相关类提供突变方法,这些方法实际上不会改变状态,而是返回一个带有修改条目的新对象:

    case class Person(val name : String, val address : String) {
       def withName(n : String) = Person(n, address)
       ...
    }
    

    这个特殊的解决方案确实涉及编码可能很长的参数列表,但仅限于类本身。它的用户很容易下车:

    val p = Person("Joe", "N St")
    val p2 = p.withName("Sam")
    ...
    

    如果您考虑到想要改变状态的原因,那么事情就会变得更加清晰。如果您正在从数据库中读取数据,您可能有很多原因需要改变对象:

    • 数据库本身已更改,您希望自动刷新内存中对象的状态
    • 您想要更新数据库本身
    • 您想传递一个对象,并通过各种方法对其进行变异

    在第一种情况下,不可变状态很容易:

    val updatedObj = oldObj.refresh
    

    第二种要复杂得多,有很多方法可以处理它(包括带有脏字段跟踪的可变状态)。看看像 Squery 这样的库是值得的,您可以在其中使用漂亮的 DSL 编写内容(请参阅 http://squeryl.org/inserts-updates-delete.html)并完全避免使用直接对象突变。

    由于复杂性的原因,您通常要避免使用最后一个。这样的事情很难并行化,难以推理,并导致各种错误,其中一个类引用另一个类,但不能保证它的稳定性。这种用法是我们正在谈论的形式的不可变状态的一种尖叫。

    【讨论】:

    【解决方案3】:

    Scala 采用了函数式编程的许多范例,其中之一是专注于使用具有不可变状态的对象。这意味着远离类中的 getter 和 setter,而是选择执行上面@Tony K. 建议的操作:当您需要更改内部对象的“状态”时,定义一个返回新 @987654321 的函数@对象。

    尝试使用不可变对象可能是 首选 Scala 方式。

    关于22参数的问题,你可以创建一个上下文类,传递给Person的构造函数:

    case class PersonContext(all: String, of: String, your: String, parameters: Int)
    class Person(context: PersonContext) extends PersonInfo { ... }
    

    如果您发现自己经常更改地址并且不想通过PersonContext rigamarole,您可以定义一个方法:

    def addressChanger(person: Person, address: String): Person = {
        val contextWithNewAddress = ...
        Person(contextWithNewAddress)
    }
    

    你可以更进一步,在Person上定义一个方法:

    class Person(context: PersonContext) extends PersonInfo {
        ...
        def newAddress(address: String): Person = {
            addressChanger(this, address)
        }
    }
    

    在您的代码中,您只需要记住,当您更新对象时,您通常会得到新的对象作为回报。一旦你习惯了这个概念,它就会变得非常自然。

    【讨论】:

      猜你喜欢
      • 2021-03-14
      • 1970-01-01
      • 2014-03-27
      • 1970-01-01
      • 2018-11-09
      • 1970-01-01
      • 1970-01-01
      • 2020-11-17
      • 2012-08-20
      相关资源
      最近更新 更多