Что делает `: _ *` (подчеркивание двоеточия) в Scala? - PullRequest
178 голосов
/ 19 мая 2011

У меня есть следующий фрагмент кода из этого вопроса :

def addChild(n: Node, newChild: Node) = n match {
  case Elem(prefix, label, attribs, scope, child @ _*) => Elem(prefix, label, attribs, scope, child ++ newChild : _*)
  case _ => error("Can only add children to elements!")
}

Все в нем довольно ясно, кроме этой части: child ++ newChild : _*

Что это делает?

Я понимаю, что есть Seq[Node], соединенный с другим Node, а затем? Что делает : _*?

Ответы [ 3 ]

138 голосов
/ 19 мая 2011

Это "сплитс" 1 последовательность.

Посмотрите на подпись конструктора

new Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding,
         child: Node*)

, которая называется

new Elem(prefix, label, attributes, scope,
         child1, child2, ... childN)

но здесь есть только последовательность, а не child1, child2 и т. д., поэтому это позволяет использовать результирующую последовательность в качестве входных данных для конструктора.

Счастливое кодирование.


1 В SLS этого имени нет, но вот подробности.Важно то, что он изменяет то, как Scala привязывает аргументы к методу с повторяющимися параметрами (как обозначено Node* выше).

_* аннотация типа покрытав «4.6.2 Повторяющиеся параметры» SLS.

Последний параметр значения раздела параметров может быть заменен на «*», например (..., x: T *).Тип такого повторяющегося параметра внутри метода является типом последовательности scala.Seq [T].Методы с повторяющимися параметрами T * принимают переменное число аргументов типа T.То есть, если метод m с типом (p1: T1, ..., pn: Tn, ps: S *) U применяется к аргументам (e1, ..., ek), где k> = n, то mв этом приложении принято иметь тип (p1: T1, ..., pn: Tn, ps: S, ..., ps0S) U, причем k ¡n вхождений типа S, где любые имена параметров, кроме ps, являются новыми. Единственное исключение из этого правила - если последний аргумент помечен как аргумент последовательности с помощью аннотации типа _ *.Если m выше применяется к аргументам (e1, ..., en, e0: _ *), то тип m в этом приложении принимается равным (p1: T1, ..., pn: Tn, ps: scala.Seq [S])

84 голосов
/ 19 мая 2011
  • child ++ newChild - последовательность
  • : - тип надписи, подсказка, которая помогает компилятору понять, к какому типу относится это выражение
  • _* - заполнитель принимаетлюбое значение + оператор vararg

child ++ newChild : _* расширяет Seq[Node] до Node* (сообщает компилятору, что мы скорее работаем с varargs, чем с последовательностью).Особенно полезно для методов, которые могут принимать только переменные.

14 голосов
/ 06 марта 2018

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

val x : Seq[Seq[Int]] = Seq(Seq(1),Seq(2))

def f(arg: Seq[Any]*) : Int = {
 arg.length
}
f(x) //1 as x is taken as single arg
f(x:_*)  // 2 as x is "unpacked" as a Seq[Any]*

Итак, теперь мы знаем, что :_* делает, чтобы сообщить компилятору: пожалуйста, распакуйте этот аргумент и привяжите эти элементы к параметру vararg в вызове функции, а не принимайте x в качестве единственного аргумента.

Таким образом, в двух словах, :_* должен устранить неоднозначность при передаче аргумента параметру vararg.

...