[SCALA] 중첩 된 구조를 업데이트 할 클리너 방법
SCALA중첩 된 구조를 업데이트 할 클리너 방법
나는이 개 경우 클래스 다음 가지고 말 :
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))
내가 주권의 우편 번호를 업데이트하려면 이제 그럼 내가해야 할 것 :
val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1))
중첩의보다 수준이 훨씬 더 추악한 가져옵니다. 이러한 중첩 된 구조를 업데이트 (Clojure에서의 갱신에 같은) 청소기 방법이 있나요?
해결법
-
==============================
1.Huet의 지퍼는 편리한 탐색 및 불변의 데이터 구조의 '돌연변이'를 제공한다. Scalaz는 스트림을위한 지퍼 (scalaz.Zipper) 및 트리 (scalaz.TreeLoc)를 제공합니다. 이 지퍼의 구조가 자동적 대수식의 기호 분화 유사한 방식으로, 원래의 데이터 구조로부터 유도 될 수있는 것으로 나타났다.
Huet의 지퍼는 편리한 탐색 및 불변의 데이터 구조의 '돌연변이'를 제공한다. Scalaz는 스트림을위한 지퍼 (scalaz.Zipper) 및 트리 (scalaz.TreeLoc)를 제공합니다. 이 지퍼의 구조가 자동적 대수식의 기호 분화 유사한 방식으로, 원래의 데이터 구조로부터 유도 될 수있는 것으로 나타났다.
그러나 방법이 당신의 스칼라의 경우 수업에 도움이됩니까? 음, 루카스 Rytz는 최근 자동 주석의 경우 클래스의 지퍼를 만들 것 scalac의 확장을 프로토 타입. 여기 자신의 예를 재현 할 수 있습니다 :
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)
그래서 사회는 이러한 노력이 계속 컴파일러에 통합되어야한다는 스칼라 팀을 설득 할 필요가있다.
덧붙여, 루카스는 최근 DSL을 통해 팩맨, 사용자 프로그램의 버전을 발표했다. 내가 어떤 @zip 주석을 볼 수 없기 때문에 그는,하지만 수정 된 컴파일러를 사용처럼 보이지 않습니다.
다른 상황에서는 구조의 어떤 시점에서 값에 대해 일치 규칙에 따라 몇 가지 전략 (하향식, 상향식 (bottom-up))에 따르면, 전체 데이터 구조를 통해 어떤 변환을 적용, 그리고 있습니다. 고전적인 예는 아마도 평가 단순화하거나 정보를 수집하기 위해, 언어에 대한 AST 변환된다. 키 아마 다시 쓰기를 지원 RewriterTests의 예제를보고,이 비디오를 시청. 다음은 식욕을 자극하는 단편이다 :
// 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))
타입 시스템 외부 키 아마 단계는이를 달성합니다.
-
==============================
2.그들은 이런 종류의 물건에 대한 MADE 된 이후 재미 아무도, 렌즈를 추가 없는지 확인합니다. 그래서, 여기에 CS 배경 종이가 여기에있다 스칼라에서 사용하는 렌즈에 간단하게 접촉 블로그입니다, 여기 Scalaz의 렌즈를 구현하고 여기에 의외로 질문처럼 보이는 그것을 사용하는 일부 코드이다. 그리고, 여기 경우 클래스 Scalaz 렌즈를 생성하는 플러그인입니다, 보일러 판을 줄일 수 있습니다.
그들은 이런 종류의 물건에 대한 MADE 된 이후 재미 아무도, 렌즈를 추가 없는지 확인합니다. 그래서, 여기에 CS 배경 종이가 여기에있다 스칼라에서 사용하는 렌즈에 간단하게 접촉 블로그입니다, 여기 Scalaz의 렌즈를 구현하고 여기에 의외로 질문처럼 보이는 그것을 사용하는 일부 코드이다. 그리고, 여기 경우 클래스 Scalaz 렌즈를 생성하는 플러그인입니다, 보일러 판을 줄일 수 있습니다.
보너스 포인트를 들어, 여기에 또 다른 S.O.입니다 렌즈에 접촉 문제, 토니 모리스의 논문.
렌즈에 대한 큰 문제는 그들이 작성 가능하다는 것이다. 그래서 그들은 처음에는 조금 복잡하지만, 그들은 더 당신이 그들을 사용 전진 계속. 또한, 그들은 당신이 개별 렌즈를 테스트 할 필요가 있기 때문에, 시험 가능성에 대한 중대하다, 그 구성을 당연시 할 수 있습니다.
그래서,이 대답의 끝에서 제공되는 구현을 기반으로, 여기 당신이 렌즈와 함께 할 것입니다 방법입니다. 첫째, 주소의 우편 번호, 그리고 사람의 주소를 변경하는 렌즈를 선언 :
val addressZipCodeLens = Lens( get = (_: Address).zipCode, set = (addr: Address, zipCode: Int) => addr.copy(zipCode = zipCode)) val personAddressLens = Lens( get = (_: Person).address, set = (p: Person, addr: Address) => p.copy(address = addr))
이제 사람의 우편 번호를 변경하는 렌즈를 얻기 위해 그들을 구성 :
val personZipCodeLens = personAddressLens andThen addressZipCodeLens
마지막으로, 주권을 변경하려면 그 렌즈를 사용합니다 :
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
또는, 일부 문법 설탕을 사용 :
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens(raj) + 1)
또는:
val updatedRaj = personZipCodeLens.mod(raj, zip => zip + 1)
다음은이 예제에 사용 Scalaz에서 가져온 간단한 구현은,이다 :
case class Lens[A,B](get: A => B, set: (A,B) => A) extends Function1[A,B] with Immutable { def apply(whole: A): B = get(whole) def updated(whole: A, part: B): A = set(whole, part) // like on immutable maps def mod(a: A, f: B => B) = set(a, f(this(a))) def compose[C](that: Lens[C,A]) = Lens[C,B]( c => this(that(c)), (c, b) => that.mod(c, set(_, b)) ) def andThen[C](that: Lens[B,C]) = that compose this }
-
==============================
3.유용한 도구는 렌즈를 사용합니다 :
유용한 도구는 렌즈를 사용합니다 :
그냥 스칼라 2.10 매크로를 기반으로 대우주와 Rillit 프로젝트, 동적 렌즈 창조를 제공하는 추가 할.
빅뱅 사용 :
case class Email(user: String, domain: String) case class Contact(email: Email, web: String) case class Person(name: String, contact: Contact) val person = Person( name = "Aki Saarinen", contact = Contact( email = Email("aki", "akisaarinen.fi"), web = "http://akisaarinen.fi" ) ) scala> Lenser[Person].contact.email.user.set(person, "john") res1: Person = Person(Aki Saarinen,Contact(Email(john,akisaarinen.fi),http://akisaarinen.fi))
대우주를 사용 :
case class Person(name: String, age: Int) val p = Person("brett", 21) scala> lens[Person].name._1(p) res1: String = brett scala> lens[Person].name._2(p, "bill") res2: Person = Person(bill,21) scala> lens[Person].namexx(()) // Compilation error
-
==============================
4.나는 좋은 구문과 최고의 기능과 여기에 언급되지 않은 하나의 라이브러리를 가지고 어떤 스칼라 라이브러리에 대한 주위를 찾고 있었어요 것은 나를 위해 정말 좋은되었습니다 단 안경입니다. 예는 다음과 같습니다
나는 좋은 구문과 최고의 기능과 여기에 언급되지 않은 하나의 라이브러리를 가지고 어떤 스칼라 라이브러리에 대한 주위를 찾고 있었어요 것은 나를 위해 정말 좋은되었습니다 단 안경입니다. 예는 다음과 같습니다
import monocle.Macro._ import monocle.syntax._ case class A(s: String) case class B(a: A) val aLens = mkLens[B, A]("a") val sLens = aLens |-> mkLens[A, String]("s") //Usage val b = B(A("hi")) val newB = b |-> sLens set("goodbye") // gives B(A("goodbye"))
이 매우 좋은이며, 렌즈를 결합하는 방법에는 여러 가지가 있습니다. 예를 들어 Scalaz는 상용구를 많이 요구하고이 빠른 컴파일과 큰 실행됩니다.
프로젝트에서 사용하는 것은 바로이 의존성에 추가
resolvers ++= Seq( "Sonatype OSS Releases" at "http://oss.sonatype.org/content/repositories/releases/", "Sonatype OSS Snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/" ) val scalaVersion = "2.11.0" // or "2.10.4" val libraryVersion = "0.4.0" // or "0.5-SNAPSHOT" libraryDependencies ++= Seq( "com.github.julien-truffaut" %% "monocle-core" % libraryVersion, "com.github.julien-truffaut" %% "monocle-generic" % libraryVersion, "com.github.julien-truffaut" %% "monocle-macro" % libraryVersion, // since 0.4.0 "com.github.julien-truffaut" %% "monocle-law" % libraryVersion % test // since 0.4.0 )
-
==============================
5.무형의 트릭을 수행합니다
무형의 트릭을 수행합니다
"com.chuusai" % "shapeless_2.11" % "2.0.0"
와:
case class Address(street: String, city: String, state: String, zipCode: Int) case class Person(firstName: String, lastName: String, address: Address) object LensSpec { import shapeless._ val zipLens = lens[Person] >> 'address >> 'zipCode val surnameLens = lens[Person] >> 'firstName val surnameZipLens = surnameLens ~ zipLens } class LensSpec extends WordSpecLike with Matchers { import LensSpec._ "Shapless Lens" should { "do the trick" in { // given some values to recreate val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg", "Mumbai", "Maharashtra", 411342)) val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1)) // when we use a lens val lensUpdatedRaj = zipLens.set(raj)(raj.address.zipCode + 1) // then it matches the explicit copy assert(lensUpdatedRaj == updatedRaj) } "better yet chain them together as a template of values to set" in { // given some values to recreate val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg", "Mumbai", "Maharashtra", 411342)) val updatedRaj = raj.copy(firstName="Rajendra", address = raj.address.copy(zipCode = raj.address.zipCode + 1)) // when we use a compound lens val lensUpdatedRaj = surnameZipLens.set(raj)("Rajendra", raj.address.zipCode+1) // then it matches the explicit copy assert(lensUpdatedRaj == updatedRaj) } } }
여기에 몇 가지 다른 답변 동안하는 것은 당신이이 shapless 렌즈 (및 다른 도서관 / 매크로) 당신이 임의의 위치에 매개 변수의 임의의 수를 설정 렌즈를 만들 수 있다는 두 가지 관련이없는 렌즈와 같은 결합하게 주어진 구조에 깊이 갈 렌즈를 구성 할 수 있습니다 당신의 구조이다. 복잡한 데이터 구조에 대한 추가 구성은 매우 유용하다.
-
==============================
6.때문에 자신의 합성 가능한 특성으로 렌즈는 많이 중첩 된 구조의 문제에 아주 좋은 솔루션을 제공합니다. 그러나 중첩의 낮은 수준, 가끔 렌즈가 조금 너무 많이하다고 생각하고, 중첩 된 업데이트 만 몇 군데있을 경우 I는 전체 렌즈는 접근 소개하고 싶지 않아요. 완벽을 위해, 여기에이 사건에 대한 매우 간단한 / 실용적인 솔루션입니다 :
때문에 자신의 합성 가능한 특성으로 렌즈는 많이 중첩 된 구조의 문제에 아주 좋은 솔루션을 제공합니다. 그러나 중첩의 낮은 수준, 가끔 렌즈가 조금 너무 많이하다고 생각하고, 중첩 된 업데이트 만 몇 군데있을 경우 I는 전체 렌즈는 접근 소개하고 싶지 않아요. 완벽을 위해, 여기에이 사건에 대한 매우 간단한 / 실용적인 솔루션입니다 :
내가하는 일은 단순히 추한 중첩 된 사본을 다루는 최상위 구조에서 몇 가지 수정 ... 도우미 기능을 작성하는 것입니다. 예를 들어 :
case class Person(firstName: String, lastName: String, address: Address) { def modifyZipCode(modifier: Int => Int) = this.copy(address = address.copy(zipCode = modifier(address.zipCode))) }
(클라이언트 측에서 업데이트를 단순화) 내 주요 목표는 달성된다 :
val updatedRaj = raj.modifyZipCode(_ => 41).modifyZipCode(_ + 1)
수정 헬퍼의 전체 세트를 만드는 것은 분명히 성가신입니다. 그러나 내부 물건 그냥 그들에게 당신이 특정 중첩 된 필드를 수정하려고 처음 작성하는 것이 괜찮습니다.
-
==============================
7.아마도 QuickLens 더 나은 질문을 일치합니다. QuickLens은 원본 문장에 가까운 무언가로는 IDE 친화적 인 식을 변환하는 매크로의 사용합니다.
아마도 QuickLens 더 나은 질문을 일치합니다. QuickLens은 원본 문장에 가까운 무언가로는 IDE 친화적 인 식을 변환하는 매크로의 사용합니다.
두 예제의 경우 클래스를 감안할 때 :
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))
당신과 주권의 우편 번호를 업데이트 할 수 있습니다 :
import com.softwaremill.quicklens._ val updatedRaj = raj.modify(_.address.zipCode).using(_ + 1)
from https://stackoverflow.com/questions/3900307/cleaner-way-to-update-nested-structures by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] 은`#`연산자는 스칼라에서 무엇을 의미합니까? (0) | 2019.10.30 |
---|---|
[SCALA] 스칼라의 VAR와 val 정의의 차이점은 무엇입니까? (0) | 2019.10.29 |
[SCALA] (왜) 우리는 캐시를 호출하거나 RDD에 유지해야합니까 (0) | 2019.10.29 |
[SCALA] 스칼라의 숨겨진 기능 (0) | 2019.10.29 |
[SCALA] 업데이트 된 행에 dataframe 행을지도하는 동안 인코더 오류 (0) | 2019.10.29 |