我有一些带有一些样板的Scala代码,我认为它是Scala,所以我一定做错了。我需要一些帮助来确定如何消除冗余。
trait Number {
val x: Int
}
case class EvenNumber(x: Int) extends Number
object EvenNumber {
def unapply(s: String): Option[EvenNumber] = {
val x = s.toInt
if (x % 2 == 0) Some(EvenNumber(x))
else None
}
}
case class OddNumber(x: Int) extends Number
object OddNumber {
def unapply(s: String): Option[OddNumber] = {
val x = s.toInt
if (x % 2 == 1) Some(OddNumber(x))
else None
}
}
在这个简单的例子中,偶数和奇数是一般数字类型的子类型。偶数和奇数都有提取器,允许从字符串创建它们。这样可以实现以下用例。
scala> "4" match {case EvenNumber(n) => n;case _ => None}
// returns EvenNumber(4)
scala> "5" match {case EvenNumber(n) => n;case _ => None}
// returns None
scala> "4" match {case OddNumber(n) => n;case _ => None}
// returns None
scala> "5" match {case OddNumber(n) => n;case _ => None}
// returns OddNumber(5)
除了x % 2
操作(0
或1
)和提取的类型(EvenNumber
或{{}的结果之外,两个提取器的源代码是相同的1}})。我希望能够编写一次源并对这两个值进行参数化,但我无法弄清楚如何。我尝试了各种类型的参数化无济于事。
Stackoverflow问题" How to use extractor in polymorphic unapply?"是相关但不同的,因为我的实现类不是通过它们包含的类型来区分,而是通过它们识别的字符串输入来区分。
以下是我在原始帖子之外收到的评论的修订版代码。 (通常情况下,第一轮答案帮助我弄清楚我的真正的问题是什么。)
OddNumber
考虑静态import scala.util.Try
trait Number {
val x: Int
}
object NumberParser {
def parse[N <: Number](s: String, remainder: Int, n: Int => N): Option[N] =
Try {s.toInt}.toOption.filter(_ % 2 == remainder).map(n(_))
}
case class EvenNumber(x: Int) extends Number
object EvenNumber {
def unapply(s: String): Option[EvenNumber] = NumberParser.parse(s, 0, EvenNumber(_))
}
case class OddNumber(x: Int) extends Number
object OddNumber {
def unapply(s: String): Option[OddNumber] = NumberParser.parse(s, 1, OddNumber(_))
}
函数是一种合理的解决方案。我仍然希望在我的所有案例类中都有语法糖来消除我重复的NumberParser.parse
行,因为在一个更复杂的例子中,有两个以上可能变得难看。有谁知道这样做的方法?
更重要的是,我真正想要支持的用例如下。
unapply
对于两种情况,这也很好,但在不同的应用程序中,如果有两种情况,它就会变得无法管理。我想写一个案例
scala> "5" match {case EvenNumber(n) =>n;case OddNumber(n) => n;case _ => None}
// returns OddNumber(5)
scala> "4" match {case EvenNumber(n) =>n;case OddNumber(n) => n;case _ => None}
// returns EvenNumber(4)
scala> "x" match {case EvenNumber(n) =>n;case OddNumber(n) => n;case _ => None}
// returns None
如上所述返回s match {case Number(n) => n; case _ => None}
,OddNumber(5)
,EvenNumber(4)
。
我无法弄清楚如何编写我的None
超类型来支持这一点。 Scala有可能吗?
编辑:在&#34; Runtime Polymorphism with Scala Extractors&#34;。
中添加了其他评论,描述了我的最终答案。答案 0 :(得分:1)
为什么继承?
object Mod2Number {
def parse[A <: Number](s: String, i: Int, n: Int => A) = {
val x = s.toInt
if (x % 2 == i) Some(n(x))
else None
}
}
case class EvenNumber(x: Int) extends Number
object EvenNumber {
def unapply(s: String) = Mod2Number.parse(s, 0, n => EvenNumber(n))
}
但如果噪音太大,你可以更进一步:
trait Makes[A <: Number] {
def apply(i: Int): A
def mod: Int
def unapply(s: String): Option[A] = {
val x = s.toInt
if (x % 2 == mod) Some(apply(x))
else None
}
}
case class EvenNumber(x: Int) extends Number
object EvenNumber extends Makes[EvenNumber] { def mod = 0 }
答案 1 :(得分:1)
我想你应该在toInt
上捕获异常 - 模式匹配中的异常是奇怪的。
object EvenNumber {
def unapply(s: String): Option[Int] = Try{s.toInt}.toOption.filter{_ % 2 == 0}
}
object OddNumber {
def unapply(s: String): Option[Int] = Try{s.toInt}.toOption.filter{_ % 2 == 1}
}
你可以提取类似的代码,但我不认为它在这里有用:
class IntFilter(f: Int => Boolean) {
def unapply(s: String): Option[Int] = Try{s.toInt}.toOption.filter(f)
}
object EvenNumber extend IntFilter(_ % 2 == 0)
object OddNumber extend IntFilter(_ % 2 == 1)
对于已修改的问题:
s match {case Number(n) => n; case _ => None}
您可以像这样创建对象Number
:
object Number{
def unapply(s: String): Option[Number] = Try{s.toInt}.toOption.collect{
case i if i % 2 == 0 => EvenNumber(i)
case i if i % 2 == 1 => OddNumber(i)
}
}