Що означає param: _ * у Scala?


87

Будучи новим у Scala (2.9.1), я маю a List[Event]і хотів би скопіювати його в a Queue[Event], але Queue[List[Event]]натомість такий синтаксис дає a :

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: _*)
викид

Ваша відповідь зрозуміла, але це насправді створює для мене більшу плутанину, як правило, у scala xs: intозначає, що тип xs - це int, проходячи по тому вищевказаному синтаксису в scala, де xs: _*означає, що xs віддається окремим членам.
Rpant

Перейшовши за вищевказаним посиланням, схоже, ось що це таке, атрибуція типу - це термінологія Scala для лиття типу Java. Будь ласка, виправте мене, якщо помилився.
Rpant

2
@ 7kemZmani: Ви повинні визначити функцію з певним типом вар-аргом: def sum(args: Int*)і ви називаєте його з підстановлювальний типу «видового» вару-арг: val a = sum(xs: _*). Подумайте про те _*, що "я передаю Int *, або рядок *, або що-небудь *, що визначено в підписі методу"
Альфонсо Нішікава,

10

Для людей з Python:

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


Приклад

Перетворення прикладу шкали за посиланням, наданим Луїджі Плінге :

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 *-operator може також мати справу з розпаковуванням позиційних параметрів / параметрів для функцій з фіксованою сутністю:

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 : _*)

не вдасться:

недостатньо аргументів для методу множення: (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.

Далі читання:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.