Что означает param: _ * в Scala?

Поскольку я новичок в Scala (2.9.1), у меня есть List[Event], и я хотел бы скопировать его в Queue[Event], но следующий синтаксис вместо этого дает Queue[List[Event]]:

val eventQueue = Queue(events)

Почему-то работает следующее:

val eventQueue = Queue(events : _*)

Но хотелось бы понять, что он делает и почему работает? Я уже смотрел подпись функции Queue.apply:

def apply[A](elems: A*)

И я понимаю, почему первая попытка не работает, но в чем смысл второй? Что такое : и _* в этом случае, и почему функция apply не принимает просто Iterable[A]?


person Chris    schedule 29.10.2011    source источник


Ответы (3)


a: A - описание типа; см. Какова цель описания типов в Scala?

: _* - это специальный экземпляр атрибута типа, который сообщает компилятору обрабатывать один аргумент типа последовательности как последовательность переменных аргументов, то есть varargs.

Вполне допустимо создать Queue, используя Queue.apply, который имеет единственный элемент, который является последовательностью или итерацией, поэтому именно это и происходит, когда вы задаете один Iterable[A].

person Ben James    schedule 29.10.2011

Это специальная нотация, которая сообщает компилятору передавать каждый элемент как отдельный аргумент, а не все как один аргумент. См. здесь.

Это аннотация типа, которая указывает аргумент последовательности и упоминается как «исключение» из общего правила в разделе 4.6.2 спецификации языка «Повторяющиеся параметры».

Это полезно, когда функция принимает переменное количество аргументов, например функция, такая как def sum(args: Int*), которая может быть вызвана как sum(1), sum(1,2) и т. д. Если у вас есть список, такой как xs = List(1,2,3), вы не можете передать сам xs, потому что это List, а не Int, но вы можете передать его элементы, используя sum(xs: _*).

person Luigi Plinge    schedule 29.10.2011
comment
def sum(xs: _*) выдает ошибку: несвязанный подстановочный знак - person 7kemZmani; 03.09.2017
comment
Ваш ответ ясен, но на самом деле это создает для меня больше путаницы, обычно в scala xs: int означает, что тип xs - int, в соответствии с этим синтаксис в scala, где xs: _* означает, что xs передается его отдельным членам. - person Rpant; 25.09.2017
comment
Перешел по приведенной выше ссылке и, похоже, вот что это такое, приписывание типа - это терминология scala для приведения типов java. Пожалуйста, поправьте меня, если ошиблись. - person Rpant; 25.09.2017
comment
@ 7kemZmani: Вы должны определить функцию с определенным типом var-args: def sum(args: Int*) и вызвать ее с помощью универсального типа var-args с подстановочными знаками: val a = sum(xs: _*). Подумайте о _*, когда я передаю Int *, String * или что-нибудь *, что определено в сигнатуре метода - person Alfonso Nishikawa; 16.01.2020

Для любителей Python:

Оператор _* в Scala более или менее эквивалентен * -оператору Python.


Пример

Преобразование примера scala из ссылки, предоставленной Луиджи Плинге:

def echo(args: String*) = 
    for (arg <- args) println(arg)

val arr = Array("What's", "up", "doc?")
echo(arr: _*)

в Python будет выглядеть так:

def echo(*args):
    for arg in args:
        print "%s" % arg

arr = ["What's", "up", "doc?"]
echo(*arr)

и оба дают следующий результат:

Что

док?


Отличие: распаковка позиционных параметров

В то время как *-оператор Python также может распаковывать позиционные параметры / параметры для функций с фиксированной арностью:

def multiply (x, y):
    return x * y

operands = (2, 4)
multiply(*operands)

8

То же самое со Scala:

def multiply(x:Int, y:Int) = {
    x * y;
}

val operands = (2, 4)
multiply (operands : _*)

не удастся:

недостаточно аргументов для метода multiply: (x: Int, y: Int) Int.
Неопределенное значение параметра y.

Но с помощью scala можно добиться того же:

def multiply(x:Int, y:Int) = {
    x*y;
}

val operands = (2, 4)
multiply _ tupled operands

Согласно Лоррин Нельсон, вот как это работает:

Первая часть, f _, представляет собой синтаксис частично применяемой функции, в которой не указан ни один из аргументов. Это работает как механизм для удержания объекта функции. tupled возвращает новую функцию arity-1, которая принимает один кортеж arity-n.

Дальнейшее чтение:

person Murmel    schedule 12.01.2018