O que param: _ * significa em Scala?

87

Sendo novo no Scala (2.9.1), tenho um List[Event]e gostaria de copiá-lo para um Queue[Event], mas a seguinte sintaxe produz um em Queue[List[Event]]vez disso:

val eventQueue = Queue(events)

Por algum motivo, o seguinte funciona:

val eventQueue = Queue(events : _*)

Mas gostaria de entender o que ele faz e por que funciona? Já olhei a assinatura da Queue.applyfunção:

def apply[A](elems: A*)

E eu entendo porque a primeira tentativa não funciona, mas qual é o significado da segunda? O que é :e _*, neste caso, e por que a applyfunção simplesmente não assume um Iterable[A]?

Chris
fonte

Respostas:

93

a: Aé a atribuição de tipo; veja Qual é o propósito das atribuições de tipo no Scala?

: _* é uma instância especial de atribuição de tipo que diz ao compilador para tratar um único argumento de um tipo de sequência como uma sequência de argumento variável, ou seja, varargs.

É totalmente válido criar um Queueusing Queue.applyque tenha um único elemento que é uma sequência ou iterável, então é exatamente isso que acontece quando você dá um único Iterable[A].

Ben James
fonte
83

Esta é uma notação especial que diz ao compilador para passar cada elemento como seu próprio argumento, em vez de tudo como um único argumento. Veja aqui .

É uma anotação de tipo que indica um argumento de sequência e é mencionada como uma "exceção" à regra geral na seção 4.6.2 da especificação de idioma, "Parâmetros repetidos".

É útil quando uma função recebe um número variável de argumentos, por exemplo, uma função como def sum(args: Int*), que pode ser invocado como sum(1), sum(1,2)etc. Se você tem uma lista, como xs = List(1,2,3), você não pode passar xs-se, porque é um Listem vez de um Int, mas você pode passar seus elementos usando sum(xs: _*).

Luigi Plinge
fonte
def sum(xs: _*)lança 'erro: tipo curinga desvinculado'
7kemZmani,
Sua resposta é clara, mas na verdade isso está criando mais confusão para mim, geralmente em scala xs: intsignifica que o tipo de xs é int, o que está acima é uma sintaxe em scala onde os xs: _*meios xs são lançados para seus membros individuais.
Rpant
Seguiu o link acima e parece que é isso mesmo, a atribuição de tipo é uma terminologia do scala para conversão de tipo java. Por favor, me corrija se estiver errado.
Rpant
1
@ 7kemZmani: Você tem que definir a função com um tipo de var-args específico: def sum(args: Int*)e você chamá-lo com o curinga tipo "genéricos" var-args: val a = sum(xs: _*). Pense _*como "Estou passando um Int *, ou uma String *, ou qualquer coisa * que esteja definida na assinatura do método"
Alfonso Nishikawa
10

Para o pessoal do Python:

O _*operador do Scala é mais ou menos equivalente ao operador * do Python .


Exemplo

Convertendo o exemplo de scala do link fornecido por Luigi Plinge :

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

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

para Python seria semelhante a:

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

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

e ambos fornecem a seguinte saída:

O que
se
passa doc?


A diferença: desempacotando parâmetros posicionais

Embora o *operador do Python também possa lidar com o desempacotamento de parâmetros / parâmetros posicionais para funções de aridade fixa:

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

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

8

Fazendo o mesmo com Scala:

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

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

vai falhar:

argumentos insuficientes para o método multiply: (x: Int, y: Int) Int.
Parâmetro de valor não especificado y.

Mas é possível conseguir o mesmo com o scala:

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

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

De acordo com Lorrin Nelson, é assim que funciona:

A primeira parte, f _, é a sintaxe para uma função parcialmente aplicada na qual nenhum dos argumentos foi especificado. Isso funciona como um mecanismo para obter o objeto de função. tupled retorna uma nova função which of arity-1 que leva uma única tupla arity-n.

Leitura futura:

Murmel
fonte