Что делает `: _ *` (подчеркивание двоеточия) в Scala?

196

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

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, а потом? Что делает : _*?

amorfis
источник
70
Большое спасибо за добавление (знак подчеркивания двоеточия) к названию!
Гал

Ответы:

152

Он "разбрызгивает" 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 Повторные параметры» СЛС.

Последний параметр значения секции параметров может быть дополнен символом «*», например (..., 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])

Роман Каган
источник
5
Нам нравится называть это «оператором Smooch», хотя на самом деле это не оператор :)
Хенрик Густафссон
1
В Python это называется распаковка
joshlk
Есть ли предел продолжительности последовательности, такой как в Java varargs?
qwwqwwq
95
  • child ++ newChild - последовательность
  • : - тип ascription, подсказка, которая помогает компилятору понять, какой тип имеет это выражение
  • _* - заполнитель, принимающий любое значение + оператор vararg

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

Васил Ременюк
источник
1
Не могли бы вы написать больше о "тип надписи"? Что это такое и как это работает?
Аморфис
10
stackoverflow.com/questions/2087250/…
Васил Ременюк
24

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

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.

Кит
источник
5

Для некоторых ленивых, таких как я, он просто конвертирует Seq в varArgs!

mani_nz
источник