Сопоставление паттерна Scala с последовательностями, отличными от списков - PullRequest
53 голосов
/ 24 июля 2011

У меня есть следующий код, который рекурсивно работает с каждым элементом в списке

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 не имеет ::, но имеет +:что, как я понимаю, в принципе одно и то же.Однако, если я пытаюсь сопоставить заголовок +: tail, компилятор жалуется: ошибка: не найдено: значение +: '
  • Nil относится только к 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 и, следовательно, разница.

Ответы [ 6 ]

51 голосов
/ 24 июля 2011

Вид мошенничества, но здесь идет речь:

def doMatch(seq: Seq[Int]): Unit = seq match {
  case Seq(x) => println("Final element " + x)
  case Seq(x, xs@_*) => println("Recursing..." + x); doMatch(xs)
}

Не спрашивайте меня, почему xs* не работает ...

48 голосов
/ 03 октября 2013

По состоянию на март 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 :

были добавлены два разных объекта головы / хвоста и зеркальное отображение начальных / последних объектов разложения
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)
24 голосов
/ 24 июля 2011

Вы действительно можете определить объект для +:, чтобы сделать именно то, что вы ищете:

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) при использовании для сопоставления паттернов.

24 голосов
/ 24 июля 2011

В Scala есть два :: (произносится минусы).Один - это оператор, определенный в class List, а другой - класс (подкласс List), представляющий непустой список, характеризуемый головой и хвостом.* - это шаблон конструктора, синтаксически измененный с ::(head, tail).

:: - это класс case, что означает, что для него определен объект экстрактора.

4 голосов
/ 24 июля 2011

Я не думаю, что в стандартной библиотеке есть поддержка сопоставления с образцом для произвольных последовательностей. Вы можете сделать это без сопоставления с шаблоном:

  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 / узел / 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)
    }
  }
}
0 голосов
/ 25 июля 2011

Простое преобразование из Seq в 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...