1. ホーム
  2. scala

[解決済み] ネストした構造体をよりきれいに更新する方法

2022-07-20 03:47:06

質問

次の2つがあるとします。 case class があるとします。

case class Address(street: String, city: String, state: String, zipCode: Int)
case class Person(firstName: String, lastName: String, address: Address)

というインスタンスがあり、次の Person クラスのインスタンスです。

val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg", 
                                           "Mumbai", 
                                           "Maharashtra", 
                                           411342))

ここで、もし私が zipCoderaj とすれば

val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1))

ネストのレベルが上がると、これはさらに醜くなります。もっときれいな方法はないでしょうか(Clojureの update-in のような)きれいな方法はありますか?

どのように解決するのですか?

ファスナー

ヒュッテ・ジッパー は、不変のデータ構造の便利な走査と'突然変異'を提供します。ScalazはZipperを Stream ( ファスナー ) と Tree ( scalaz.TreeLoc ). ジッパーの構造は、代数式の記号的微分のような方法で、元のデータ構造から自動的に導出できることがわかりました。

しかし、これはScalaのケースクラスでどのように役立つのでしょうか?さて、Lukas Rytz は最近 をプロトタイプ化しました。 この拡張は,アノテーションされた case class に対して自動的にジッパーを作成します.彼の例をここに再現してみる。

scala> @zip case class Pacman(lives: Int = 3, superMode: Boolean = false) 
scala> @zip case class Game(state: String = "pause", pacman: Pacman = Pacman()) 
scala> val g = Game() 
g: Game = Game("pause",Pacman(3,false))

// Changing the game state to "run" is simple using the copy method:
scala> val g1 = g.copy(state = "run") 
g1: Game = Game("run",Pacman(3,false))

// However, changing pacman's super mode is much more cumbersome (and it gets worse for deeper structures):
scala> val g2 = g1.copy(pacman = g1.pacman.copy(superMode = true))
g2: Game = Game("run",Pacman(3,true))

// Using the compiler-generated location classes this gets much easier: 
scala> val g3 = g1.loc.pacman.superMode set true
g3: Game = Game("run",Pacman(3,true)

ですから、コミュニティはこの努力を継続し、コンパイラに統合するよう、Scalaチームを説得する必要があります。

ちなみに、Lukasは最近 を公開しました。 DSL を使ってユーザがプログラム可能なパックマンのバージョンを発表しました。彼は修正されたコンパイラを使ったようには見えませんが、私はどんな @zip の注釈が見当たらないからです。

ツリーのリライト

他の状況では、ある戦略(トップダウン、ボトムアップ)に従って、データ構造全体に何らかの変換を適用し、構造内のある点での値に対して一致するルールに基づいて、変換したい場合があります。古典的な例としては、ある言語のASTを、評価、簡略化、情報収集のために変換することが挙げられます。 Kiama サポート リライト の例を参照してください。 リライターテスト を、そしてこの ビデオ . 以下は、食欲をそそるスニペットです。

// Test expression
val e = Mul (Num (1), Add (Sub (Var ("hello"), Num (2)), Var ("harold")))

// Increment every double
val incint = everywheretd (rule { case d : Double => d + 1 })
val r1 = Mul (Num (2), Add (Sub (Var ("hello"), Num (3)), Var ("harold")))
expect (r1) (rewrite (incint) (e))

なお、Kiama 外に出る を実現するために、型システムの外に出ています。