是否可以在一个匹配语句中运行多个提取器?
object CoolStuff {
def unapply(thing: Thing): Option[SomeInfo] = ...
}
object NeatStuff {
def unapply(thing: Thing): Option[OtherInfo] = ...
}
// is there some syntax similar to this?
thing match {
case t @ CoolStuff(someInfo) @ NeatStuff(otherInfo) => process(someInfo, otherInfo)
case _ => // neither Cool nor Neat
}
这里的意图是有两个提取器,我不必做这样的事情:
object CoolNeatStuff {
def unapply(thing: Thing): Option[(SomeInfo, OtherInfo)] = thing match {
case CoolStuff(someInfo) => thing match {
case NeatStuff(otherInfo) => Some(someInfo -> otherInfo)
case _ => None // Cool, but not Neat
case _ => None// neither Cool nor Neat
}
}
答案 0 :(得分:6)
可以尝试
object ~ {
def unapply[T](that: T): Option[(T,T)] = Some(that -> that)
}
def too(t: Thing) = t match {
case CoolStuff(a) ~ NeatStuff(b) => ???
}
答案 1 :(得分:2)
我提出了一个非常类似的解决方案,但我有点太慢了,所以我没有把它作为答案发布。但是,由于@userunknown要求解释它是如何工作的,所以无论如何我都会在这里转储我的类似代码,并添加一些注释。也许有人发现它是cchantep的简约解决方案的一个有价值的补充(看起来...... 书法?由于某种原因,在很好的意义上)。
所以,这是我类似的,美学上不太令人满意的提议:
object && {
def unapply[A](a: A) = Some((a, a))
}
// added some definitions to make your question-code work
type Thing = String
type SomeInfo = String
type OtherInfo = String
object CoolStuff {
def unapply(thing: Thing): Option[SomeInfo] = Some(thing.toLowerCase)
}
object NeatStuff {
def unapply(thing: Thing): Option[OtherInfo] = Some(thing.toUpperCase)
}
def process(a: SomeInfo, b: OtherInfo) = s"[$a, $b]"
val res = "helloworld" match {
case CoolStuff(someInfo) && NeatStuff(otherInfo) =>
process(someInfo, otherInfo)
case _ =>
}
println(res)
打印
[helloworld, HELLOWORLD]
我们的想法是,标识符(特别是cchantep代码中的&&
和~
)可以用作infix operators in patterns。因此,match-case
case CoolStuff(someInfo) && NeatStuff(otherInfo) =>
将被嫁入
case &&(CoolStuff(someInfo), NeatStuff(otherInfo)) =>
然后将调用unapply
的{{1}}方法方法,它只是复制其输入。
在我的代码中,复制是通过简单的&&
实现的。在cchantep的代码中,使用较少的括号来完成:Some((a, a))
。箭头Some(t -> t)
来自ArrowAssoc,后者又作为->
中的隐式转化提供。这只是创建通常在地图中使用的对的快速方法:
Predef
另一句话:注意Map("hello" -> 42, "world" -> 58)
可以多次使用:
&&
所以...我不知道这是对cchantep答案的答案还是扩展评论,但也许有人觉得它很有用。
答案 2 :(得分:1)
对于那些可能会错过有关此魔术实际上如何工作的细节的人,只想扩展@cchantep anf @Andrey Tyukin的答案(注释部分不允许我这样做)。
使用 -Xprint:解析器 选项运行 scalac 会产生一些效果( scalac 2.11.12 )
def too(t: String) = t match {
case $tilde(CoolStuff((a @ _)), NeatStuff((b @ _))) => $qmark$qmark$qmark
}
这基本上向您展示了编译器在将源解析为AST时执行的初始步骤。
重要说明此处是Infix Operation Patterns和Extractor Patterns中描述了编译器进行此转换的规则。特别是,这允许您使用任何具有unapply
方法的对象,例如CoolStuff(a) AndAlso NeatStuff(b)
。在先前的答案中,&&
和~
也尽可能被选为,但不是唯一可用的有效标识符。
如果运行带有选项 -Xprint: patmat 的 scalac ,这是翻译模式匹配的特殊阶段可以看到与此类似的东西
def too(t: String): Nothing = {
case <synthetic> val x1: String = t;
case9(){
<synthetic> val o13: Option[(String, String)] = main.this.~.unapply[String](x1);
if (o13.isEmpty.unary_!)
{
<synthetic> val p3: String = o13.get._1;
<synthetic> val p4: String = o13.get._2;
{
<synthetic> val o12: Option[String] = main.this.CoolStuff.unapply(p3);
if (o12.isEmpty.unary_!)
{
<synthetic> val o11: Option[String] = main.this.NeatStuff.unapply(p4);
if (o11.isEmpty.unary_!)
matchEnd8(scala.this.Predef.???)
此处~.unapply
将在输入参数t
上调用,这将产生Some((t,t))。元组值将被提取到变量p3
和p4
中。然后,将调用CoolStuff.unapply(p3)
,如果结果不是None
NeatStuff.unapply(p4)
,则调用a
并检查其是否不为空。如果两者都不为空,则根据Variable Patterns b
和Some
将绑定到相应{{1}}内的返回结果。