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

87

Поскольку я новичок в 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]?

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

Ответы:

93

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

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

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

Бен Джеймс
источник
83

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

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

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

Луиджи Плиндж
источник
def sum(xs: _*)выдает 'error: unbound wildcard type'
7kemZmani
Ваш ответ ясен, но это на самом деле создает для меня больше путаницы, обычно в scala xs: intозначает, что тип xs - int, в соответствии с этим синтаксис в scala выше xs: _*означает, что xs передается его отдельным членам.
Rpant
Следуя приведенной выше ссылке и похоже, что это то, что это такое, приписывание типа - это терминология scala для приведения типов java. Пожалуйста, поправьте меня, если ошиблись.
Rpant
2
@ 7kemZmani: Вы должны определить функцию с определенным типом вар-аргом: def sum(args: Int*)и вы называете его с подстановочным типа «видового» вара-арг: val a = sum(xs: _*). Подумайте об этом _*так: «Я передаю Int *, String * или что-нибудь *, что определено в сигнатуре метода»
Альфонсо Нисикава
10

Для людей 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.

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

Мурмель
источник