2

我有以下数据模型,稍后我将对其进行模式匹配:

abstract class A
case class C(s:String) extends A

abstract class B extends A
case class D(i:Int) extends B
case class E(s:Int, e:Int) extends B

A 是层次结构的抽象超类型。C 是 A 的具体子类。A 的其他具体子类是 B 的子类,而 B 又是 A 的子类。

现在,如果我写这样的东西,它的工作原理:

def match(a:A) a match {
   a:C => println("C")
   a:B => println("B")
}

但是,在 for 循环中,我无法匹配 B。我假设我需要一个构造函数模式,但由于 B 是抽象的,因此 B 没有构造函数模式。

val list:List[A] = List(C("a"), D(1), E(2,5), ...)

for (b:B <- list) println(b)  // Compile error
for (b@B <- list) println(b)  // Compile error

在这里,我只想打印 B 个实例。这种情况有什么解决方法吗?

4

4 回答 4

3

您可以使用collect

list.collect { case b: B => println(b) }

如果您想更好地理解这一点,我建议您阅读部分函数。以这里为例。

于 2013-01-25T11:15:10.453 回答
2

谢尔盖是对的;for如果您只想模式匹配和过滤B实例,您将不得不放弃。如果您出于某种原因想使用for理解,我认为一种方法是使用警卫:

for (b <- list if b.isInstanceOf[B]) println(b)

但总是最好选择模式匹配而不是isInstanceOf. 所以我会接受这个collect建议(如果它在我的其余代码的上下文中有意义的话)。

另一个建议是定义一个具有相同名称的伴随对象,并定义方法:Bunapply

abstract class A
case class C(s:String) extends A

abstract class B extends A
object B { def unapply(b: B) = Option(b)  } // Added a companion to B
case class D(i:Int) extends B
case class E(s:Int, e:Int) extends B

然后你可以这样做:

for (B(b) <- list) println(b)

所以这不是 B 的“构造函数”,而是同伴的unapply方法。它有效,这就是朋友的目的,对吧?

(见http://www.scala-lang.org/node/112

于 2013-01-25T11:35:49.620 回答
2

如果你问我,你不能在这里使用模式匹配的事实是不幸的 scala 不一致。确实,scala确实让您可以对推导进行模式匹配,如本例所示:

val list:List[A] = List(C("a"), D(1), E(2,5)
for ((b:B,_) <- list.map(_ -> null)) println(b)

在这里,我暂时将元素包装成对(带有一个虚拟且未使用的第二个值),然后对第一个元素为 B 类型的一对进行模式匹配。如输出所示,您将获得预期的行为:

 D(1)
 E(2,5)

所以你去了,scala确实支持基于模式匹配的过滤(即使按类型匹配),似乎语法不处理按类型匹配单个元素的模式。

显然我不建议使用这个技巧,这只是为了说明。使用collect肯定更好。

再说一次,如果由于某种原因你真的更喜欢理解,还有另一个更通用的解决方案:

object Value {
  def unapply[T]( value: T ) = Some( value )
}
for ( Value(b:B) <- list ) println(b)

我们只是在对象中引入了一个虚拟提取器,Value它什么都不做,所以它Value(b:B)与 just 具有相同的含义,只是b:B前者确实编译。与我之前的对子技巧不同,它相对易读,Value只需要编写一次,你可以随意使用它(特别是,不需要为你想要模式匹配的每种类型编写一个新的提取器,因为在@Faiz 的回答中。我会让你找到一个更好的名字Value

最后,还有另一种开箱即用的解决方法(感谢 Daniel Sobral),但可读性稍差,需要一个虚拟标识符(此处foo):

for ( b @(foo:B) <- list ) println(b)
// or similarly:
for ( foo @(b:B) <- list ) println(b)
于 2013-01-25T13:08:02.633 回答
0

我的 2 美分:您可以在 for comprehension 检查类型中添加一个条件,但这不会像使用collect只接受 B 类实例那样优雅。

于 2013-01-25T11:39:59.920 回答