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

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

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

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

4b9b3361

Ответ 1

Он "отображает" 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 Это не имеет cutesy-name в 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])

Ответ 2

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

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

Ответ 3

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

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.

Ответ 4

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