Scala模式与集合匹配

Scala模式与集合匹配,scala,pattern-matching,Scala,Pattern Matching,下面的方法不起作用 object Foo { def union(s: Set[Int], t: Set[Int]): Set[Int] = t match { case isEmpty => s case (x:xs) => union(s + x, xs) case _ => throw new Error("bad input") } } 错误:未找到:类型xs 如何在一个集合上进行模式匹配?

下面的方法不起作用

object Foo {
    def union(s: Set[Int], t: Set[Int]): Set[Int] = t match {
        case isEmpty => s
        case (x:xs)  => union(s + x, xs)
        case _       => throw new Error("bad input")
    }
}
错误:未找到:类型xs


如何在一个集合上进行模式匹配?

首先,您的
isEmpty
将捕获每个
集合,因为它在这个上下文中是一个变量。常量在Scala中以大写字母开头,并且只有在该条件成立时才被视为常量。因此,小写字母会将任何
集合
分配给
isEmpty
(您是否正在寻找
清空集
?)

如图所示,模式匹配对于
Set
s似乎不是很好。您可能应该明确地将
集合
转换为
列表
序列
toList
/
toSeq


Set
不是
case类
,也没有
unapply
方法

这两件事意味着您不能在
集合上直接匹配模式
(更新:除非您为
定义了自己的提取器,正如Daniel在回答中正确显示的那样)

你应该找到另一种选择,我建议使用折叠函数

def union(s: Set[Int], t: Set[Int]): Set[Int] = 
    (s foldLeft t) {case (t: Set[Int], x: Int) => t + x}
或者,避免最显式的类型注释

def union(s: Set[Int], t: Set[Int]): Set[Int] =
  (s foldLeft t)( (union, element) => union + element )
甚至更短

def union(s: Set[Int], t: Set[Int]): Set[Int] =
  (s foldLeft t)(_ + _)
这将在
t
上累加
s
的元素,并逐个添加它们


折叠

以下是折叠操作的步骤,如需参考:

foldLeft[B](z: B)(op: (B, A) ⇒ B): B
将二进制运算符应用于起始值和该集合的所有元素,从左到右

注意:可能会为不同的运行返回不同的结果,除非基础集合类型已排序。或者算符是结合的和交换的

B the result type of the binary operator.
z the start value.
op the binary operator.
returns the result of inserting op between consecutive elements of this set, going left to right with the start value z on the left:

op(...op(z, x_1), x_2, ..., x_n)
where x1, ..., xn are the elements of this set.

嗯,
x:xs
表示类型为
xs
x
,所以它不起作用。但是,遗憾的是,您无法对匹配集进行模式化,因为集合没有定义的顺序。或者更实用地说,因为
Set
上没有提取器

不过,您始终可以定义自己的:

object SetExtractor {
  def unapplySeq[T](s: Set[T]): Option[Seq[T]] = Some(s.toSeq)
}
例如:

scala> Set(1, 2, 3) match {
     |   case SetExtractor(x, xs @ _*) => println(s"x: $x\nxs: $xs")
     | }
x: 1
xs: ArrayBuffer(2, 3)

这就是我能想到的:

object Contains {
  class Unapplier[T](val t: T) {
    def unapply(s: Set[T]): Option[Boolean] = Some(s contains t)
  }
  def apply[T](t: T) = new Unapplier(t)
}

object SET {
  class Unapplier[T](val set: Set[T]) {
    def unapply(s: Set[T]): Option[Unit] = if (set == s) Some(Unit) else None
  }
  def apply[T](ts: T*) = new Unapplier(ts.toSet)
}

val Contains2 = Contains(2)
val SET123 = SET(1, 2, 3)

Set(1, 2, 3) match {
  case SET123()         => println("123")
  case Contains2(true)  => println("jippy")
  case Contains2(false) => println("ohh noo")
}

还有与值匹配的反勾选项。是否由于将
集合
转换为
列表
而导致任何慢度?是。为了提高性能,您可以使用宝塔的解决方案。我只是想告诉你如何使用模式匹配。顺便说一句,在我的示例中,
\uuu
大小写永远不会到达,因此如果
Set
类没有
unappy()
方法,那么它就不能通过匹配来分解?正确,具有
unapply(…)
方法的对象称为提取器,其目的是定义如何在
match/case
块中提取其参数。对于
案例类
,这是自动的。如果可能的话,你可以参考《Scala第二版编程》第26章。作为Scala新手,我花了一些时间对折叠函数的语法感到困惑。当我把第二行写成
t.foldLeft(s)((s,t_el)=>s+t_el)
时,我发现它更容易理解,因为它符合定义(此外,在本例中,更简洁)@MauriceNaftalin我不记得为什么我写得这么明确,但我会按照你的建议更新答案。为了让Kevin明白一点:因为集合上没有定义顺序,所以不应该将SetExtractor与实际值一起使用,例如
case SetExtractor(1,xs@*])=>…
;它恰好与
Set(1,2,3)
一起工作,但通常不起作用,例如与
Set(1,2,3,4,5)
一起工作。Daniel将此作为一种允许解构绑定从集合中拾取任意元素的方法。另外请注意,余数xs是一个数组缓冲区,因此如果希望将其作为一个集合,请使用
xs.toSet
。另请参见:at 0:39:45
object Contains {
  class Unapplier[T](val t: T) {
    def unapply(s: Set[T]): Option[Boolean] = Some(s contains t)
  }
  def apply[T](t: T) = new Unapplier(t)
}

object SET {
  class Unapplier[T](val set: Set[T]) {
    def unapply(s: Set[T]): Option[Unit] = if (set == s) Some(Unit) else None
  }
  def apply[T](ts: T*) = new Unapplier(ts.toSet)
}

val Contains2 = Contains(2)
val SET123 = SET(1, 2, 3)

Set(1, 2, 3) match {
  case SET123()         => println("123")
  case Contains2(true)  => println("jippy")
  case Contains2(false) => println("ohh noo")
}
    t match {
      case s if s.nonEmpty => // non-empty 
      case _ => // empty
    }