Как извлечь остаток последовательности при сопоставлении с образцом

Я, очевидно, очень плохо справился с объяснением того, что я ищу в моем исходном сообщении, поэтому давайте попробуем еще раз время. То, что я пытаюсь достичь, - это возможность передать последовательность элементов, извлечь один или несколько элементов, а затем передать REMAINDER последовательности другому экстрактору. Обратите внимание, что под последовательностью я подразумеваю последовательность (не обязательно список). В моих предыдущих примерах в качестве последовательности использовался список, и я привел несколько примеров извлечения с использованием cons (: :), но я мог бы с таким же успехом передать массив в качестве своей последовательности.

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

Если я хочу вернуть один элемент из мой экстрактор я бы определил неприменимый метод. Этот метод принимает любой тип, который я выбрал в качестве входных (тип может быть последовательностью ...), и возвращает единственный необязательный элемент (тип возврата сам может быть последовательностью). Возврат должен быть завернут в Some, если я хочу совпадение, или None, если я не хочу. Вот пример, который принимает последовательность в качестве входных данных и возвращает ту же последовательность, заключенную в Some, но только если она содержит все строки. Я вполне мог бы просто вернуть последовательность, завернутую в Some, и больше ничего не делать, но это, кажется, сбивает людей с толку. Ключ в том, что если он завернут в Some, он будет соответствовать, а если это None, то не будет. Чтобы быть более ясным, совпадение также не произойдет, если ввод также не соответствует моему типу ввода неприменимых методов. Вот мой пример:

object Test {
  // In my original post I just returned the Seq itself just to verify I 
  // had matched but many people commented they didn't understand what I 
  // was trying to do so I've made it a bit more complicated (e.g. match 
  // only if the sequence is a sequence of Strings). Hopefully I don't 
  // screw this up and introduce a bug :)
  def unapply[A](xs: Seq[A]): Option[Seq[String]] = 
    if (xs forall { _.isInstanceOf[String] })
      Some(xs.asInstanceOf[Seq[String]])
    else
      None
}

Используя List в качестве примера, теперь я могу выполнить следующее:

// This works
def test1(xs: List[_]) = xs match {
  case (s: String) :: Test(rest) =>
    println("s = " + s + ", rest = " + rest)
  case _ =>
    println("no match")
}

test1(List("foo", "bar", "baz"))  // "s = foo, rest = List(bar, baz)"

Моя функция test1 принимает List в качестве входных данных и извлекает голову и хвост, используя cons через шаблон конструктора (например: :( s, отдыхать)). Затем он использует описание типа (: String), чтобы убедиться, что заголовок (и) является String. Хвост содержит List («бар», «баз»). Это список, что означает, что это также Seq (последовательность). Затем он передается в качестве входных данных моему экстрактору Test, который проверяет, что и bar, и baz являются строками, и возвращает List, завернутый в Some.Поскольку Some возвращается, это считается совпадением (хотя в моем исходном сообщении, где я случайно перепутал unapplySeq с unapply, это не сработало, как ожидалось, но это в стороне ...). Это НЕ то, что я ищу. Это был всего лишь пример, показывающий, что Test действительно извлекает Seq в качестве входных данных, как и ожидалось.

Вот где я в прошлый раз вызвал массовую путаницу, когда случайно использовал unapplySeq вместо unapply в моем описании. После долгой путаницы, пытаясь разобраться в опубликованных комментариях, я наконец понял ошибку. Большое спасибо Дэну за то, что он указал мне в правильном направлении ...

Но просто во избежание путаницы, позвольте мне прояснить мое понимание unapplySeq. Как и unapply, unapplySeq принимает любой аргумент, который я выбираю в качестве входных, но вместо возврата одного элемента он возвращает последовательность элементов. Затем каждый элемент в этой последовательности можно использовать для дополнительного сопоставления с образцом. Опять же, чтобы совпадение произошло, тип ввода должен совпадать, а моя возвращаемая последовательность должна быть заключена в Some, а не на None. При извлечении из последовательности элементов, возвращенных unapplySeq, вы можете использовать _ * для сопоставления любых оставшихся элементов, еще не сопоставленных.

Итак, мой экстрактор принимает последовательность в качестве входных данных и возвращает последовательность (как отдельный элемент) взамен. Поскольку я хочу вернуть только один элемент в качестве совпадения, мне нужно использовать unapply NOT unapplySeq. Несмотря на то, что в моем случае я возвращаю Seq, я не хочу unapplySeq, потому что я не хочу выполнять больше сопоставлений с образцом для элементов в Seq. Я просто хочу вернуть элементы как Seq самостоятельно, чтобы затем передать их в тело моего совпадения case.Это звучит сбивающе с толку, но я надеюсь, что для тех, кто понимает unapply и unapplySeq, это не так.

Итак, вот что Я ХОЧУ сделать. Я хочу взять что-то, что возвращает последовательность (например, List или Array), и я хочу извлечь несколько элементов из этой последовательности, а затем извлечь REMAINDER элементов (например, _ *) в виде последовательности. Назовем это остаточной последовательностью.Затем я хочу передать оставшуюся последовательность в качестве входных данных для моего экстрактора. Затем мой экстрактор вернет оставшиеся элементы как один Seq, если он соответствует моим критериям. Просто чтобы быть на 100% ясным. Для списка (или массива и т. Д.) Будет вызван экстрактор unapplySeq для создания последовательности элементов. Я извлечу один или несколько из этих элементов, а затем передам то, что осталось в виде последовательности, моему экстрактору Test, который будет использовать unapply (НЕ unapplySeq) для возврата остатка. Если вас это смущает, не комментируйте ...

Вот мои тесты:

// Doesn't compile. Is there a syntax for this?
def test2(xs: Seq[_]) = xs match {
  // Variations tried:
  //   Test(rest) @ _*  - doesn't compile (this one seems reasonable to me)
  //   Test(rest @ _*)  - doesn't compile (would compile if Test had 
  //                      unapplySeq, but in that case would bind List's
  //                      second element to Test as a Seq and then bind 
  //                      rest to that Seq (if all strings) - not what I'm
  //                      looking for...). I though that this might work
  //                      since Scala knows Test has no unapplySeq only 
  //                      unapply so @ _* can be tied to the List not Test
  //   rest @ Test(_*)  - doesn't compile (didn't expect to)
  case List(s: String, Test(rest) @ _*) => 
    println("s = " + s + " rest = " + rest)
  case _ =>
    println("no match")
}

// This works, but messy
def test3(xs: List[_]) = xs match {
  case List(s: String, rest @ _*) if (
    rest match { case Test(rest) => true; case _ => false }
  ) => 
    println("s = " + s + " rest = " + rest)
  case _ =>
    println("no match")
}

Я создал test3 на основе комментариев Джулиана (спасибо, Джулиан ..). Некоторые прокомментировали, что test3 делает то, что я хочу, поэтому они не понимают, что я ищу. Да, он выполняет то, что я хочу достичь, но меня это не устраивает. Пример Дэниела также работает (спасибо Дэниелу), но меня также не устраивает необходимость создания другого экстрактора для разделения вещей, а затем выполнения встроенных извлечений. Эти решения кажутся слишком трудоемкими для достижения чего-то, что мне кажется довольно простым. Я ХОЧУ, чтобы test2 работал или знал, что это невозможно. Ошибка возникает из-за неправильного синтаксиса? Я знаю, что rest @ _ * вернет Seq, который можно проверить здесь:

def test4(xs: List[_]) = xs match {
  case List(s: String, rest @ _*) => 
    println(rest.getClass)   // scala.collection.immutable.$colon$colon
  case _ =>
    println("no match")
}

Он возвращает cons (: :), который является списком, который является Seq. Итак, как я могу передать _ * Seq своему экстрактору и вернуть его обратно к переменной rest?

Обратите внимание, что я также пробовал передавать varargs в свой неприменимый конструктор (например, unapply (xs: A *) ...), но это тоже не совпадало.

Итак, я надеюсь, что теперь понятно, когда я говорю, что хочу извлечь остаток последовательности при сопоставлении с образцом. Я не знаю, как еще я могу это описать.

Основываясь на отличном отклике Даниэля, я надеюсь, что он найдет для меня ответ :)

5
задан 13 revs 28 November 2011 в 06:53
поделиться