Я, очевидно, очень плохо объяснил, что я ищу в своем оригинальном посте, поэтому давайте попробуем это еще раз. То, что я пытаюсь выполнить, - это возможность передать последовательность элементов, извлечь один или несколько элементов, а затем передать 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
}
Используя Список в качестве примера, теперь я могу выполнить следующее:
// 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, rest)). Затем он использует тип ascription (: String), чтобы убедиться, что заголовок (и) является String. Хвост содержит список («bar», «baz»). Это список, что означает, что это также Seq (последовательность). Затем он передается в качестве входных данных моему экстрактору тестов, который проверяет, что и "bar", и "baz" являются строками, и возвращает список, заключенный в Some. Так как Some возвращается, это считается совпадением (хотя в моем первоначальном посте, где я случайно перепутал unapplySeq с unapply, это не сработало, как ожидалось, но это в сторону ...). Это НЕ то, что я ищу. Это был только пример, демонстрирующий, что Test фактически извлекает Seq в качестве входных данных, как и ожидалось.
Теперь, вот где я в прошлый раз вызвал массовое замешательство, когда по неосторожности использовал unapplySeq вместо unapply в моей статье. После большого беспорядка, пытаясь понять комментарии, которые были отправлены, я наконец понял ошибку. Большое спасибо Дэну за то, что он указал мне правильное направление ...
Но только избегайте путаницы, позвольте мне прояснить мое понимание unapplySeq. Как и unapply, unapplySeq принимает любой аргумент, который я выбрал в качестве входных данных, но вместо возврата одного элемента он возвращает последовательность элементов. Каждый элемент в этой последовательности может затем использоваться для дополнительного сопоставления с образцом. Опять же, чтобы совпадение произошло, тип ввода должен совпадать, и моя возвращенная последовательность должна быть заключена в Some, а не в None. При извлечении последовательности элементов, возвращаемых из unapplySeq, вы можете использовать _ *, чтобы сопоставить любые оставшиеся элементы, которые еще не были сопоставлены.
Хорошо, мой экстрактор принимает последовательность в качестве входных данных и возвращает последовательность (как один элемент) в ответ. Поскольку я хочу вернуть только один элемент в качестве совпадения, мне нужно использовать unapply NOT unapplySeq. Несмотря на то, что в моем случае я возвращаю Seq, я не хочу unapplySeq, потому что я не хочу больше сопоставлять шаблоны с элементами в Seq. Я просто хочу вернуть элементы в виде Seq самостоятельно, чтобы затем передать их в тело моего совпадения. Это звучит странно, но для тех, кто понимает, неприменимы против неприменимы. Надеюсь, это не так.
Так вот, что я хочу сделать. Я хочу взять что-то, что возвращает последовательность (например, List или Array), и я хочу извлечь несколько элементов из этой последовательности, а затем извлечь REMAINDER из элементов (например, _ *) в виде последовательности. Давайте назовем это остатком последовательности. Затем я хочу передать оставшуюся последовательность в качестве входных данных моему экстрактору. Мой экстрактор вернет оставшиеся элементы в виде одного Seq, если это соответствует моим критериям. Просто чтобы быть на 100% ясным. У List (или Array и т. Д.) Будет вызываться экстрактор unapplySeq для создания последовательности элементов. Я извлеку один или несколько из этих элементов, а затем передам то, что осталось в виде последовательности, в мой тестовый экстрактор, который будет использовать 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 (: :), который является List, который является Seq. Так как же я могу передать _ * Seq на мой экстрактор и получить привязку возврата к переменной rest?
Обратите внимание, что я также пытался передать varargs моему неприменимому конструктору (например, unapply (xs: A *) ...), но это тоже не будет соответствовать.
Итак, я надеюсь, что теперь ясно, когда я скажу, что хочу извлечь остаток последовательности в сопоставлении с образцом. Я не уверен, как еще я могу это произнести.
Исходя из отличного отзыва от Даниэля, я надеюсь, что у него будет ответ для меня:)