2

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 になることです。私が理解しているように、scalaではvalが「優先」されています。このタイプの例で、30 個以上の引数のいずれかが変更されるたびにすべての引数を書き直すことなく、val をどのように機能させることができるでしょうか?

つまり、私は持っている可能性があります

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, アドレス)

有望に見えましたが、PersonInfo を拡張するクラスがかなりあります。つまり、簡単なことをするために、定義を再実装し、多くのタイピングとカット アンド ペーストを行う必要があります。特性 PersonInfo を通常のクラスに変換し、その中にすべての定義を入れると、PersonInfo ではなく Person を返すにはどうすればよいかという問題があります。どういうわけかトレイトまたはスーパークラスに実装し、すべてのサブクラスを実際に拡張する賢いスカラはありますか?

私が見る限り、例が非常に単純で、2 つまたは 3 つのパラメーターの場合、scala ではすべてが非常にうまく機能しますが、数十のパラメーターがある場合、非常に退屈で機能しなくなります。

oddcanada の PersonContext も似たようなものだと思いますが、これはまだ考え中です。43 個のパラメーターがある場合、パラメーターを Person に送り込むためだけに、複数の一時クラスに分割する必要があると思います。

コピー オプションも興味深いもので、難解ですが、タイピングははるかに少なくなります。

Javaから来て、scalaからの巧妙なトリックを期待していました。

4

3 に答える 3

10

ケース クラスには、これに使用する定義済みのcopyメソッドがあります。

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

val mike = Person("Mike", 42)

val newMike = mike.copy(age = 43)

これはどのように作動しますか?は、コンパイラーが作成copyするメソッド ( など) の 1 つにすぎません。この例では、次のとおりです。equalshashCode

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

このメソッドの値とは、外側のスコープの値を隠しますnameageご覧のとおり、デフォルト値が提供されているため、変更したい値のみを指定する必要があります。他のものは、現在のインスタンスにあるものにデフォルト設定されます。

于 2012-12-13T05:41:59.523 に答える
5

scala に var が存在する理由は、可変状態をサポートするためです。場合によっては、変更可能な状態が本当に必要なものです (たとえば、パフォーマンスや明確さの理由から)。

ただし、不変状態の使用を奨励する背景には、多くの証拠と経験があることは間違いありません。物事は多くの面でうまく機能します (並行性、理由の明確さなど)。

あなたの質問に対する答えの 1 つは、実際には状態を変更せず、変更されたエントリを持つ新しいオブジェクトを返すミューテーター メソッドを問題のクラスに提供することです。

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

2 つ目はもっと複雑で、処理方法はたくさんあります (ダーティ フィールド トラッキングによる変更可能な状態を含む)。Squery のようなライブラリを調べると、良い DSL で記述でき ( http://squeryl.org/inserts-updates-delete.htmlを参照)、オブジェクトの直接的な変更を完全に避けることができます。

最後の 1 つは、複雑さの理由から一般的に避けたいものです。そのようなことは、並列化が難しく、推論が難しく、あるクラスが別のクラスへの参照を持っているというあらゆる種類のバグにつながりますが、その安定性についての保証はありません. この種の使用法は、私たちが話しているフォームの不変状態を叫ぶものです。

于 2012-12-13T04:30:07.793 に答える
0

Scala は関数型プログラミングから多くのパラダイムを採用しており、そのうちの 1 つは不変状態のオブジェクトを使用することに重点を置いています。これは、クラス内のゲッターとセッターから離れ、代わりに上記の @Tony K. が提案したことを行うことを選択することを意味します: 内部オブジェクトの「状態」を変更する必要がある場合は、新しいPersonオブジェクトを返す関数を定義します。

不変オブジェクトを使用しようとすることは、Scalaの推奨される方法である可能性があります。

22 個のパラメーターの問題に関しては、Person のコンストラクターに渡されるコンテキスト クラスを作成できます。

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

アドレスを頻繁に変更し、PersonContextリガマロールを通過する必要がない場合は、メソッドを定義できます。

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

これをさらに進めて、 on でメソッドを定義できますPerson

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

コードでは、オブジェクトを更新しているときに、しばしば新しいオブジェクトが返されることを覚えておく必要があります。その概念に慣れると、それは非常に自然になります。

于 2012-12-13T05:14:19.763 に答える