Scala Соответствие шаблону с наборами

Не работает следующее.

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")
    }
}

error: not found: type xs

Как я могу сопоставить шаблон по набору?

Ответ 1

Ну, x:xs означает x типа xs, поэтому он не сработает. Но, увы, вы не можете создавать сопоставления наборов, потому что у наборов нет определенного порядка. Или более прагматично, потому что на 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)

Ответ 2

Set не является case class и не имеет unapply метода.

Эти две вещи подразумевают, что вы не можете напрямую сопоставлять паттерны в Set.
(обновление: если вы не определите свой собственный экстрактор для Set, как правильно показывает Даниэль в своем ответе)

Вы должны найти альтернативу, я бы предложил использовать функцию сгиба

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)(_ + _)

Это накапливает элементы s по t, добавляя их один за другим


складной

Вот документы для операции сгиба, если необходимо для справки:

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.

Ответ 3

Прежде всего, ваш isEmpty будет ловить каждый Set, так как это переменная в этом контексте. Константы начинаются с буквы верхнего регистра в Scala и рассматриваются только как константы, если это условие выполнено. Таким образом, строчные буквы будут присваивать любые Set to isEmpty (искали ли вы EmptySet?)

Как видно здесь, похоже, что сопоставление шаблонов не очень предпочтительнее для Set s. Вероятно, вы должны явно преобразовать Set в List или Seq (toList/toSeq)

object Foo {
    def union(s: Set[Int], t: Set[Int]): Set[Int] = t.toList match {
        case Nil => s
        case (x::xs)  => union(s + x, xs.toSet)
        case _       => throw new Error("bad input")
    }
}

Ответ 4

Вот что я могу придумать:

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")
}

Ответ 5

    t match {
      case s if s.nonEmpty => // non-empty 
      case _ => // empty
    }