Подтвердить что ты не робот

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

4b9b3361

Ответ 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)