Scala сопоставление шаблонов по последовательностям, отличным от списков
У меня есть следующий код, который рекурсивно работает с каждым элементом в List
def doMatch(list: List[Int]): Unit = list match {
case last :: Nil => println("Final element.")
case head :: tail => println("Recursing..."); doMatch(tail)
}
Теперь, игнорируя, что эта функция доступна через filter() и foreach(), это работает отлично. Однако, если я попытаюсь изменить его, чтобы принять любой Seq [Int], я столкнулся с проблемами:
- Seq не имеет::, но он имеет +:, который, как я понимаю, в основном то же самое. Если я пытаюсь сопоставить на голове +: хвост, однако, компилятор жалуется на ошибку: не найден: значение +: '
- Ниль специфичен для List, и я не уверен, что заменить его. Я собираюсь попробовать Seq(), если я когда-нибудь пройду предыдущую проблему.
Вот как я думаю, код должен выглядеть, кроме того, что он не работает:
def doMatch(seq: Seq[Int]): Unit = seq match {
case last +: Seq() => println("Final element.")
case head +: tail => println("Recursing..."); doMatch(tail)
}
Изменить: так много хороших ответов! Я принимаю ответ agilesteel, поскольку он был первым, который отметил, что:: не является оператором в моем примере, но класс case и, следовательно, разница.
Ответы
Ответ 1
В Scala есть два ::
(произносится как cons). Один из них - оператор, определенный в class List
, а один - класс (подкласс List
), который представляет собой непустой список, характеризующийся головой и хвостом.
head :: tail
- это шаблон конструктора, который синтаксически модифицирован из ::(head, tail)
.
::
- это класс case, что означает, что для него определен объект экстрактора.
Ответ 2
Вид обмана, но вот он идет:
def doMatch(seq: Seq[Int]): Unit = seq match {
case Seq(x) => println("Final element " + x)
case Seq(x, [email protected]_*) => println("Recursing..." + x); doMatch(xs)
}
Не спрашивайте меня, почему xs*
не работает...
Ответ 3
Начиная с марта 2012 года, это работает в 2.10 +:
def doMatch(seq: Seq[Int]): Unit = seq match {
case last +: Seq() => println("Final element.")
case head +: tail => println("Recursing..."); doMatch(tail)
} //> doMatch: (seq: Seq[Int])Unit
doMatch(List(1, 2)) //> Recursing...
//| Final element.
В общем случае для Seq
в SeqExtractors добавлены два разных объекта head/tail и init/last decposition, отражающие append/prepend:
List(1, 2) match { case init :+ last => last } //> res0: Int = 2
List(1, 2) match { case head +: tail => tail } //> res1: List[Int] = List(2)
Vector(1, 2) match { case init :+ last => last } //> res2: Int = 2
Vector(1, 2) match { case head +: tail => tail } //> res3: scala.collection.immutable.Vector[Int] = Vector(2)
Ответ 4
Фактически вы можете определить объект для +:
, чтобы выполнить именно то, что вы ищете:
object +: {
def unapply[T](s: Seq[T]) =
if(s.nonEmpty)
Some(s.head, s.tail)
else
None
}
scala> val h +: t = Seq(1,2,3)
h: Int = 1
t: Seq[Int] = List(2, 3)
Затем ваш код работает точно так, как ожидалось.
Это работает, потому что h +: t
эквивалентно +:(h,t)
при использовании для сопоставления patten.
Ответ 5
Я не думаю, что существует поддержка соответствия шаблону для произвольных последовательностей в стандартной библиотеке. Вы могли бы сделать это с отсутствием соответствия шаблону:
def doMatch(seq: Seq[Int]) {
if (seq.size == 1) println("final element " + seq(0)) else {
println("recursing")
doMatch(seq.tail)
}
}
doMatch(1 to 10)
Однако вы можете определить свои собственные объекты-экстракторы. См. http://www.scala-lang.org/node/112
object SEQ {
def unapply[A](s:Seq[A]):Option[(A, Seq[A])] = {
if (s.size == 0) None else {
Some((s.head, s.tail))
}
}
}
def doMatch(seq: Seq[Int]) {
seq match {
case SEQ(head, Seq()) => println("final")
case SEQ(head, tail) => {
println("recursing")
doMatch(tail)
}
}
}
Ответ 6
Простая трансформация из Seq to List будет выполнять следующее задание:
def doMatch (list: List[Int]): Unit = list match {
case last :: Nil => println ("Final element.")
case head :: tail => println ("Recursing..."); doMatch (tail)
case Nil => println ("only seen for empty lists")
}
def doMatchSeq (seq: Seq[Int]) : Unit = doMatch (seq.toList)
doMatch (List(3, 4, 5))
doMatchSeq (3 to 5)