Scala case class 带动态字段的复制构造函数
Scala case class copy constructor with dynamic fields
我有一个不可变的 State
,我处理一个消息队列,其中每条消息都是状态中某些字段的新值列表。
新值可能适用于字段的一部分 - 例如设置或清除多个标志(位)中的一个,或者仅更改 16 位字段的低 8 位或高 8 位部分。
处理消息后,我想获得状态的不可变副本,并应用修改。
object StateField {
sealed abstract class StateField()
sealed abstract class Register extends StateField
sealed abstract class Flag extends StateField
case object AX extends Register
case object AH extends Register
case object AL extends Register
case object CF extends Flag
case object OF extends Flag
}
class StateFieldModification(field: StateField, value: Int)
class ModificationMessage(content: List[StateFieldModification])
case class State(AX: Int, Flags: Int) {
def readRegister(field: StateField.Register): Int = field match {
case StateField.AX => this.AX
case StateField.AH => this.AX & 0xFF
case StateField.AL => (this.AX << 8) & 0xFF
}
def readFlag(field: StateField.Flag): Boolean = field match {
case StateField.CF => (this.Flags & 0x0001) != 0
case StateField.OF => (this.Flags & 0x0800) != 0
}
def flagsWithBit(flagBit: Int, newBitValue: Boolean): Int = {
if (newBitValue) Flags | (1 << flagBit) else Flags & ~(1 << flagBit)
}
def withModification(modification: StateFieldModification): State = modification.field match {
case StateField.AX => this.copy(AX = modification.value)
case StateField.AH => this.copy(AX = (this.AX & 0x00FF) | (modification.value << 8))
case StateField.AL => this.copy(AX = (this.AX & 0xFF00) | modification.value)
case StateField.CF => this.copy(Flags = flagsWithBit(1, modification.value > 0))
case StateField.CF => this.copy(Flags = flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage) = ???
}
Q#1 - 根据 "field key" 创建字段 getter 的最佳方法是什么?
Q#2 - 根据 "field key" 创建字段 setter 的最佳方法是什么?
Q#3 - 给定可变性消息,创建新对象的最佳方法是什么?
请注意:
- 我对 Java 有经验,但刚开始使用 Scala。
- 我正在做一个干净的-sheet项目,因此非常欢迎您对代码的任何部分发表评论,任何内容都可以重写。
- 上面的代码可以工作,但感觉与 Scala 的优雅应该有的样子相去甚远。
- 我不想使用反射,如这里所建议的:
Scala case class copy with dynamic named parameter
- 我 不想 使用字段的键值映射而不是真正的 class 字段,就像 Python 那样。
感谢所有帮助。谢谢!!!
你好像做错了。这将是一种采用动态语言(如 perl 或 ruby)的方法,但 scala 不是这样。您可能可以使用 Scala 来模仿类似的东西,但它会很难让您不想这样做。
.getDetail
不是你在 Scala 中经常看到的东西。如果你想得到一个人的名字,你通常会 person.name
而不是 person.getDetail(PersonDetail.Name)
。为什么?好吧,为什么不呢?当你可以做前者时,根本没有理由做后者。
对于 setter 也是如此:person.copy(firstName = "foo") 比 person.withModiciation(PersonDetail.Name, "foo")
效果更好
第三种情况可能是最复杂的。如果您想应用一大堆修改怎么办?
好吧,我仍然会争辩说
val list = List(
PersonDetail.FirstName -> "foo",
PersonDetail.LastName -> "bar",
PersonDetail.OtherStuff -> "baz"
)
person.withModifications(list)
并不比 scala 的习惯好多少
person.copy(firstName = "foo", lastName = "bar", otherStuff = "baz")
您是否考虑过使用 Lens 库 such as Monacle? Lens 是功能抽象,允许您将功能应用于数据结构的一部分,例如您的 Person 案例 class。 Lens 可以放大结构的一部分,所以对于 Person,可以
import monocle.Lens
val firstName = Lens[Person, String]( p => f => p.copy(firstName = f))
val newPerson = firstName.set("someNewName")(person)
这样PersonDetail中的每一个修改都可以对应一个合适的Lens。镜头也支持其他操作。对于更复杂的修改,Lens 可以按照 Monacle README 中所示进行组合。除了调用 set 函数外,他们还可以根据当前值修改数据,以对应 FirstNameFirstLetter 的情况。
firstName.headOption.modify(_.toUpper)(person)
Lenses 形式化了数据结构具有 getter 和非变异 'setter' 函数的概念,但我们不需要形式主义来获得一些优势。我们将使用控制反转为您的 class 创建修饰函数。这些修饰函数将编码如下想法:
'Give me the current state and a new AX, and I will give you a new state'
'Give me the current state and a function that calculates a new AX from the current AX, and I will give you a new state'
'Give me the current state and the new flags, and I will give you a new state'
所以,代码:
case class State(ax: Int, flags: Int) {
private def setAx(newAx: Int): State = copy(ax = newAx)
private def modAx(f: Int => Int): State = setAx(f(ax))
private def setFlags(newFlags: Int): State = copy(flags = newFlags)
def withModification(modification: StateFieldModification): State =
modification.field match {
case StateField.AX => setAx(modification.value)
case StateField.AH =>
modAx { ax => (ax & 0x00FF) | (modification.value << 8) }
case StateField.AL =>
modAx { ax => (ax & 0xFF00) | modification.value }
case StateField.CF =>
setFlags(flagsWithBit(1, modification.value > 0))
case StateField.CF =>
setFlags(flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage): State =
message.content.foldLeft(this) { (state, modification) =>
state withModification modification
}
}
P.S.,你可能可以简化你的一些类型,例如StateField
并不需要是多级层次结构——您可以将其拆分为单独的 Register
和 Flag
枚举;你可以解包或输入 alias ModificationMessage
因为它只是一个列表。
我有一个不可变的 State
,我处理一个消息队列,其中每条消息都是状态中某些字段的新值列表。
新值可能适用于字段的一部分 - 例如设置或清除多个标志(位)中的一个,或者仅更改 16 位字段的低 8 位或高 8 位部分。
处理消息后,我想获得状态的不可变副本,并应用修改。
object StateField {
sealed abstract class StateField()
sealed abstract class Register extends StateField
sealed abstract class Flag extends StateField
case object AX extends Register
case object AH extends Register
case object AL extends Register
case object CF extends Flag
case object OF extends Flag
}
class StateFieldModification(field: StateField, value: Int)
class ModificationMessage(content: List[StateFieldModification])
case class State(AX: Int, Flags: Int) {
def readRegister(field: StateField.Register): Int = field match {
case StateField.AX => this.AX
case StateField.AH => this.AX & 0xFF
case StateField.AL => (this.AX << 8) & 0xFF
}
def readFlag(field: StateField.Flag): Boolean = field match {
case StateField.CF => (this.Flags & 0x0001) != 0
case StateField.OF => (this.Flags & 0x0800) != 0
}
def flagsWithBit(flagBit: Int, newBitValue: Boolean): Int = {
if (newBitValue) Flags | (1 << flagBit) else Flags & ~(1 << flagBit)
}
def withModification(modification: StateFieldModification): State = modification.field match {
case StateField.AX => this.copy(AX = modification.value)
case StateField.AH => this.copy(AX = (this.AX & 0x00FF) | (modification.value << 8))
case StateField.AL => this.copy(AX = (this.AX & 0xFF00) | modification.value)
case StateField.CF => this.copy(Flags = flagsWithBit(1, modification.value > 0))
case StateField.CF => this.copy(Flags = flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage) = ???
}
Q#1 - 根据 "field key" 创建字段 getter 的最佳方法是什么?
Q#2 - 根据 "field key" 创建字段 setter 的最佳方法是什么?
Q#3 - 给定可变性消息,创建新对象的最佳方法是什么?
请注意:
- 我对 Java 有经验,但刚开始使用 Scala。
- 我正在做一个干净的-sheet项目,因此非常欢迎您对代码的任何部分发表评论,任何内容都可以重写。
- 上面的代码可以工作,但感觉与 Scala 的优雅应该有的样子相去甚远。
- 我不想使用反射,如这里所建议的: Scala case class copy with dynamic named parameter
- 我 不想 使用字段的键值映射而不是真正的 class 字段,就像 Python 那样。
感谢所有帮助。谢谢!!!
你好像做错了。这将是一种采用动态语言(如 perl 或 ruby)的方法,但 scala 不是这样。您可能可以使用 Scala 来模仿类似的东西,但它会很难让您不想这样做。
.getDetail
不是你在 Scala 中经常看到的东西。如果你想得到一个人的名字,你通常会 person.name
而不是 person.getDetail(PersonDetail.Name)
。为什么?好吧,为什么不呢?当你可以做前者时,根本没有理由做后者。
对于 setter 也是如此:person.copy(firstName = "foo") 比 person.withModiciation(PersonDetail.Name, "foo")
第三种情况可能是最复杂的。如果您想应用一大堆修改怎么办? 好吧,我仍然会争辩说
val list = List(
PersonDetail.FirstName -> "foo",
PersonDetail.LastName -> "bar",
PersonDetail.OtherStuff -> "baz"
)
person.withModifications(list)
并不比 scala 的习惯好多少
person.copy(firstName = "foo", lastName = "bar", otherStuff = "baz")
您是否考虑过使用 Lens 库 such as Monacle? Lens 是功能抽象,允许您将功能应用于数据结构的一部分,例如您的 Person 案例 class。 Lens 可以放大结构的一部分,所以对于 Person,可以
import monocle.Lens
val firstName = Lens[Person, String]( p => f => p.copy(firstName = f))
val newPerson = firstName.set("someNewName")(person)
这样PersonDetail中的每一个修改都可以对应一个合适的Lens。镜头也支持其他操作。对于更复杂的修改,Lens 可以按照 Monacle README 中所示进行组合。除了调用 set 函数外,他们还可以根据当前值修改数据,以对应 FirstNameFirstLetter 的情况。
firstName.headOption.modify(_.toUpper)(person)
Lenses 形式化了数据结构具有 getter 和非变异 'setter' 函数的概念,但我们不需要形式主义来获得一些优势。我们将使用控制反转为您的 class 创建修饰函数。这些修饰函数将编码如下想法:
'Give me the current state and a new AX, and I will give you a new state'
'Give me the current state and a function that calculates a new AX from the current AX, and I will give you a new state'
'Give me the current state and the new flags, and I will give you a new state'
所以,代码:
case class State(ax: Int, flags: Int) {
private def setAx(newAx: Int): State = copy(ax = newAx)
private def modAx(f: Int => Int): State = setAx(f(ax))
private def setFlags(newFlags: Int): State = copy(flags = newFlags)
def withModification(modification: StateFieldModification): State =
modification.field match {
case StateField.AX => setAx(modification.value)
case StateField.AH =>
modAx { ax => (ax & 0x00FF) | (modification.value << 8) }
case StateField.AL =>
modAx { ax => (ax & 0xFF00) | modification.value }
case StateField.CF =>
setFlags(flagsWithBit(1, modification.value > 0))
case StateField.CF =>
setFlags(flagsWithBit(12, modification.value > 0))
}
def withModifications(message: ModificationMessage): State =
message.content.foldLeft(this) { (state, modification) =>
state withModification modification
}
}
P.S.,你可能可以简化你的一些类型,例如StateField
并不需要是多级层次结构——您可以将其拆分为单独的 Register
和 Flag
枚举;你可以解包或输入 alias ModificationMessage
因为它只是一个列表。