Scala:如何模式匹配内部案例 class 的封闭对象?
Scala: How to pattern-match the enclosing object of an inner case class?
我有一个内部案例 class,特别是来自 this 问题的一个事件,我想匹配它,包括外部对象:
class Player {
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def execute() = _life += life
}
}
我可以轻松编写一个效果(部分函数)来替换特定玩家的生活事件:
//gain twice as much life
def effect(player: Player): ReplacementEffect = {
case player.GainLife(x) => player.GainLife(x * 2)
}
但是,我不能对其他玩家做同样的事情。我最接近的是:
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case evt: Player#GainLife => player.GainLife(evt.life)
}
但是 1) 这甚至用新的生命增益替换了你自己的生命增益,2) 我无法引用最初在函数中获得生命的玩家,以及 3) 我错过了直接匹配 life
这样。
这可以使用与路径无关的类型来表达,例如
case Player.GainLife(_player, life) if _player != player => GainLife(player, life)
理想情况下,我想要
case _player.GainLife(life) if _player != player => player.GainLife(life)
这是否可能,或者我可以解决这个问题吗?还是我必须求助于嵌套 GainLife?
当您在另一个内部定义 class 时,这意味着该类型特定于周围的 class,因此 playerA.GainLife 与 playerB.GainLife 不同(这称为路径相关类型)如果您希望它表示相同的含义,您可以在相同的范围内定义它,而不管实例如何:包或 class.
的伴随对象
您可以在这个问题中阅读更多内容:What is meant by Scala's path-dependent types?
我最接近的是定义我自己的unapply
方法:
class Player {
self =>
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def player = self
def execute() = _life += life
}
}
object Player {
object _GainLife {
def unapply(event: Player#GainLife) =
Some((event.player, event.life))
}
}
请注意,将 Player._GainLife
对象命名为 Player.GainLife
会导致名称冲突,这是这里最重要的缺点。因此,我选择在 Player
命名空间之外提供该类型:
val GainLife = Player._GainLife
这允许以简洁的方式同时使用 player.GainLife.unapply 和 Player._GainLife.unapply 进行匹配:
//gain twice as much life
def effect1(player: Player): ReplacementEffect = {
case player.GainLife(life) => player.GainLife(life * 2)
}
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case GainLife(_player, life) if _player != player => player.GainLife(life)
}
//all players gain twice as much life
def effect3: ReplacementEffect = {
case GainLife(player, life) => player.GainLife(life * 2)
}
最后两个示例看起来有点不对称,但如果需要,可以使用 apply
方法修复。
我有一个内部案例 class,特别是来自 this 问题的一个事件,我想匹配它,包括外部对象:
class Player {
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def execute() = _life += life
}
}
我可以轻松编写一个效果(部分函数)来替换特定玩家的生活事件:
//gain twice as much life
def effect(player: Player): ReplacementEffect = {
case player.GainLife(x) => player.GainLife(x * 2)
}
但是,我不能对其他玩家做同样的事情。我最接近的是:
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case evt: Player#GainLife => player.GainLife(evt.life)
}
但是 1) 这甚至用新的生命增益替换了你自己的生命增益,2) 我无法引用最初在函数中获得生命的玩家,以及 3) 我错过了直接匹配 life
这样。
这可以使用与路径无关的类型来表达,例如
case Player.GainLife(_player, life) if _player != player => GainLife(player, life)
理想情况下,我想要
case _player.GainLife(life) if _player != player => player.GainLife(life)
这是否可能,或者我可以解决这个问题吗?还是我必须求助于嵌套 GainLife?
当您在另一个内部定义 class 时,这意味着该类型特定于周围的 class,因此 playerA.GainLife 与 playerB.GainLife 不同(这称为路径相关类型)如果您希望它表示相同的含义,您可以在相同的范围内定义它,而不管实例如何:包或 class.
的伴随对象您可以在这个问题中阅读更多内容:What is meant by Scala's path-dependent types?
我最接近的是定义我自己的unapply
方法:
class Player {
self =>
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def player = self
def execute() = _life += life
}
}
object Player {
object _GainLife {
def unapply(event: Player#GainLife) =
Some((event.player, event.life))
}
}
请注意,将 Player._GainLife
对象命名为 Player.GainLife
会导致名称冲突,这是这里最重要的缺点。因此,我选择在 Player
命名空间之外提供该类型:
val GainLife = Player._GainLife
这允许以简洁的方式同时使用 player.GainLife.unapply 和 Player._GainLife.unapply 进行匹配:
//gain twice as much life
def effect1(player: Player): ReplacementEffect = {
case player.GainLife(life) => player.GainLife(life * 2)
}
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case GainLife(_player, life) if _player != player => player.GainLife(life)
}
//all players gain twice as much life
def effect3: ReplacementEffect = {
case GainLife(player, life) => player.GainLife(life * 2)
}
最后两个示例看起来有点不对称,但如果需要,可以使用 apply
方法修复。