为什么Scala有unapply和unapplySeq?两者有什么区别?我什么时候应该更喜欢一个?
考虑这个课程:
class DateTime(year: Int, month: Int, day: Int)(hour: Int, minute: Int, second: Int)
Run Code Online (Sandbox Code Playgroud)
该unapply方法将如何,如果我想匹配以下内容:
dt match {
case DateTime(2012, 12, 12)(12, _, _) => // December 12th 2012, 12 o'clock
/* ... */
}
Run Code Online (Sandbox Code Playgroud)
我试过这个:
def unapply(dt: DateTime) =
Some((dt.year, dt.month, dt.day),(dt.hour, dt.minute, dt.second))
Run Code Online (Sandbox Code Playgroud)
但这并没有真正起作用.
我试图了解Scala对Case Classes的作用,使它们对类型擦除警告有所不同.
假设我们有以下简单的类结构.它基本上是Either:
abstract class BlackOrWhite[A, B]
case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]
case class White[A,B]( val right: B ) extends BlackOrWhite[A,B]
Run Code Online (Sandbox Code Playgroud)
而你正试图像这样使用它:
object Main extends App {
def echo[A,B] ( input: BlackOrWhite[A,B] ) = input match {
case Black(left) => println( "Black: " + left )
case White(right) => println( "White: " + right )
}
echo( Black[String, Int]( "String!" ) )
echo( White[String, Int]( 1234 ) )
}
Run Code Online (Sandbox Code Playgroud)
一切都编译和运行没有任何问题.但是,当我unapply自己尝试实现该方法时,编译器会发出警告.我使用了以下类结构与Main上面相同的类: …
在以下简化的示例代码中:
case class One[A](a: A) // An identity functor
case class Twice[F[_], A](a: F[A], b: F[A]) // A functor transformer
type Twice1[F[_]] = ({type L[?] = Twice[F, ?]}) // We'll use Twice1[F]#L when we'd like to write Twice[F]
trait Applicative[F[_]] // Members omitted
val applicativeOne: Applicative[One] = null // Implementation omitted
def applicativeTwice[F[_]](implicit inner: Applicative[F]): Applicative[({type L[?] = Twice[F, ?]})#L] = null
Run Code Online (Sandbox Code Playgroud)
我可以在applicativeOne上调用applicativeTwice,并且类型推断工作,一旦我尝试在applicativeTwice(applicativeOne)上调用它,推理就会失败:
val aOK = applicativeTwice(applicativeOne)
val bOK = applicativeTwice[Twice1[One]#L](applicativeTwice(applicativeOne))
val cFAILS = applicativeTwice(applicativeTwice(applicativeOne))
Run Code Online (Sandbox Code Playgroud)
scala 2.10.0中的错误是
- type mismatch; …Run Code Online (Sandbox Code Playgroud) 我不能在unapply提取器的方法上使用泛型以及隐式"转换器"来支持特定于参数化类型的模式匹配吗?
我想这样做(注意使用[T]就unapply行),
trait StringDecoder[A] {
def fromString(string: String): Option[A]
}
object ExampleExtractor {
def unapply[T](a: String)(implicit evidence: StringDecoder[T]): Option[T] = {
evidence.fromString(a)
}
}
object Example extends App {
implicit val stringDecoder = new StringDecoder[String] {
def fromString(string: String): Option[String] = Some(string)
}
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor[String](x) => x // <- type hint barfs …Run Code Online (Sandbox Code Playgroud) 基本上,我希望能够构建一个自定义提取器,而无需在使用它之前将其存储在变量中.
这不是我将如何使用它的一个真实示例,它更可能用于正则表达式或其他字符串模式(如构造),但希望它能解释我正在寻找的内容:
def someExtractorBuilder(arg:Boolean) = new {
def unapply(s:String):Option[String] = if(arg) Some(s) else None
}
//I would like to be able to use something like this
val {someExtractorBuilder(true)}(result) = "test"
"test" match {case {someExtractorBuilder(true)}(result) => result }
//instead I would have to do this:
val customExtractor = someExtractorBuilder(true)
val customExtractor(result) = "test"
"test" match {case customExtractor(result) => result}
Run Code Online (Sandbox Code Playgroud)
当只做一个自定义提取器时,它没有太大的区别,但如果你为case语句构建一个大的提取器列表,它可能会通过将所有提取器与它们的使用分开来使事情变得更难.
我希望答案是不,你不能这样做,但我想我先问一下:D
L1下面的对象有效.我可以L1通过传入varargs 来"创建" ,这很好,但我希望能够L1使用相同的语法分配给一个.不幸的是,我在这里完成它的方式需要更粗略的嵌套Array内部语法L1.
object L1 {
def apply(stuff: String*) = stuff.mkString(",")
def unapply(s: String) = Some(s.split(","))
}
val x1 = L1("1", "2", "3")
val L1(Array(a, b, c)) = x1
println("a=%s, b=%s, c=%s".format(a,b,c))
Run Code Online (Sandbox Code Playgroud)
我尝试以一种显而易见的方式实现这一目标,L2如下所示:
object L2 {
def apply(stuff: String*) = stuff.mkString(",")
def unapply(s: String) = Some(s.split(","):_*)
}
val x2 = L2("4", "5", "6")
val L2(d,e,f) = x2
println("d=%s, e=%s, f=%s".format(d,e,f))
Run Code Online (Sandbox Code Playgroud)
但是这给出了错误:
error: no `: _*' annotation allowed here
(such …Run Code Online (Sandbox Code Playgroud) 我想要一个提取器来隐式转换它的参数,但它似乎不起作用.考虑这个非常简单的情况:
case class MyString(s: String) {}
implicit def string2mystring(x: String): MyString = new MyString(x)
implicit def mystring2string(x: MyString) = x.s
object Apply {
def unapply(s: MyString): Option[String] = Some(s)
}
Run Code Online (Sandbox Code Playgroud)
但是我无法像我期望的那样使用它:
val Apply(z) = "a" // error: scrutinee is incompatible with pattern type
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释为什么它无法将参数转换String为MyString?我希望它可以string2mystring("a")即时通话.很明显,我可以解决这个问题val Apply(y) = MyString("a"),但似乎我不应该这样做.
注意:这个问题类似于这一个,而是1)一个不确实对为什么发生这种情况,2)的例子是比它需要更加复杂的一个很好的答案.
我希望能够这样做:
scala> val Int(i) = "1"
i: Int = 1
Run Code Online (Sandbox Code Playgroud)
但是Int没有unapply方法.
我找到了这个答案,它给出了如何隐式地将方法添加到现有对象的说明,所以我试了一下.他们提供的解决方案有效,但遗憾的是不适用于模式匹配.这就是我所拥有的:
object UnapplyInt {
val IntRE = """^(\d+)$""".r
def unapply(v: String): Option[Int] = v match {
case IntRE(s) => Some(s.toInt)
case _ => None
}
}
implicit def int2unapplyInt(objA: Int.type) = UnapplyInt
Run Code Online (Sandbox Code Playgroud)
这些测试用例都很好:
val UnapplyInt(i) = "1" // pattern matching with unapply is fine
val i = Int.unapply("1").get // implicit conversion is fine
Run Code Online (Sandbox Code Playgroud)
但我想要的那个失败了:
scala> val Int(i) = "1"
<console>:10: error: object Int …Run Code Online (Sandbox Code Playgroud) 我真的没有得到这个小东西.我有一个抽象类Box,有几个不同类型的子类.例如
abstract class Box
class StringBox(val sValue : String) extends Box
Run Code Online (Sandbox Code Playgroud)
Box的伴侣对象中的apply方法很简单:
object Box{
def apply(s: String) = new StringBox(s)
def apply(b: Boolean) = new BooleanBox(b)
def apply(d: Double) = new DoubleBox(d)
}
Run Code Online (Sandbox Code Playgroud)
所以我可以写
val sb = Box("StringBox)
Run Code Online (Sandbox Code Playgroud)
好的,写不应用会带来一些麻烦.我的第一个想法是在类型上使用模式匹配,如下所示:
def unapply(b: Box) = b match {
case sb: StringBox => Some(sb.sValue)
case bb: BooleanBox => Some(bb.bValue)
case db: DoubleBox => Some(db.dValue)
case _ => None
Run Code Online (Sandbox Code Playgroud)
}
由于类型擦除,这根本不起作用.
第二次尝试是具有类型T的通用Box [T],并且在每个子类中重新定义了抽象类型成员.例如:
abstract class Box[T] {def value : T}
class StringBox(val sValue : …Run Code Online (Sandbox Code Playgroud)