Quais são todos os usos de um sublinhado no Scala?


Respostas:


576

Os que eu consigo pensar são

Tipos existenciais

def foo(l: List[Option[_]]) = ...

Parâmetros do tipo de classificação superior

case class A[K[_],T](a: K[T])

Variáveis ​​ignoradas

val _ = 5

Parâmetros ignorados

List(1, 2, 3) foreach { _ => println("Hi") }

Nomes ignorados de tipos próprios

trait MySeq { _: Seq[_] => }

Padrões curinga

Some(5) match { case Some(_) => println("Yes") }

Padrões curinga em interpolações

"abc" match { case s"a$_c" => }

Curinga da sequência nos padrões

C(1, 2, 3) match { case C(vs @ _*) => vs.foreach(f(_)) }

Importações curinga

import java.util._

Ocultando importações

import java.util.{ArrayList => _, _}

Associando cartas aos operadores

def bang_!(x: Int) = 5

Operadores de atribuição

def foo_=(x: Int) { ... }

Sintaxe do espaço reservado

List(1, 2, 3) map (_ + 2)

Valores do método

List(1, 2, 3) foreach println _

Convertendo parâmetros de chamada por nome em funções

def toFunction(callByName: => Int): () => Int = callByName _

Inicializador padrão

var x: String = _   // unloved syntax may be eliminated

Pode haver outros que eu esqueci!


Exemplo mostrando o porquê foo(_)e foo _são diferentes:

Este exemplo vem de 0__ :

trait PlaceholderExample {
  def process[A](f: A => Unit)

  val set: Set[_ => Unit]

  set.foreach(process _) // Error 
  set.foreach(process(_)) // No Error
}

No primeiro caso, process _representa um método; Scala usa o método polimórfico e tenta torná-lo monomórfico preenchendo o parâmetro type, mas percebe que não há um tipo que possa ser preenchido para Aque dê o tipo (_ => Unit) => ?(Existential _não é um tipo).

No segundo caso, process(_)é um lambda; ao escrever um lambda sem nenhum tipo de argumento explícito, Scala deduz o tipo do argumento que foreachespera e _ => Unit é um tipo (enquanto simplesmente _não é), para que possa ser substituído e inferido.

Essa pode ser a pegadinha mais complicada de Scala que eu já encontrei.

Observe que este exemplo compila na 2.13. Ignore-o como se estivesse atribuído a sublinhado.


4
Acho que existem dois ou três que se encaixam no uso de sublinhado na correspondência de padrões, mas +1 para juntar letras à pontuação! :-)
Daniel C. Sobral

22
val x: Any = _
Giovanni Botta

2
@ Owen: Eu não acho que println _ é uma função parcialmente aplicada. É outro exemplo de sintaxe de espaço reservado, certo? Significado mapa (_ + 2) se expande para algo semelhante para mapear (x => x + 2), assim como pritnln (_) se expande para algo semelhante para mapear (x => println (x))
Andrew Cassidy

7
@AndrewCassidy Na verdade println _e println(_)são diferentes. Você pode ver isso, por exemplo, no fato de eles lidarem com tipos existenciais e polimórficos de maneira um pouco diferente. Virá com um exemplo daqui a pouco.
Owen

3
@AndrewCassidy OK Adicionei um exemplo.
Owen

179

De (minha entrada) na FAQ , que certamente não garanto que esteja completa (adicionei duas entradas há apenas dois dias):

import scala._    // Wild card -- all of Scala is imported
import scala.{ Predef => _, _ } // Exception, everything except Predef
def f[M[_]]       // Higher kinded type parameter
def f(m: M[_])    // Existential type
_ + _             // Anonymous function placeholder parameter
m _               // Eta expansion of method into method value
m(_)              // Partial function application
_ => 5            // Discarded parameter
case _ =>         // Wild card pattern -- matches anything
val (a, _) = (1, 2) // same thing
for (_ <- 1 to 10)  // same thing
f(xs: _*)         // Sequence xs is passed as multiple parameters to f(ys: T*)
case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence
var i: Int = _    // Initialization to the default value
def abc_<>!       // An underscore must separate alphanumerics from symbols on identifiers
t._2              // Part of a method name, such as tuple getters
1_000_000         // Numeric literal separator (Scala 2.13+)

Isso também faz parte desta pergunta .


2
Pode ser que você pode adicionar var i: Int = _ou o caso especial de correspondência de padrão val (a, _) = (1, 2)ou o caso especial de val descartadofor (_ <- 1 to 10) doIt()
huynhjl

1
E def f: T; def f_=(t: T)combinação para criar membro f mutável.
precisa saber é o seguinte

A correspondência de padrões já está coberta e os _nomes dos métodos estão trapaceando. Mas tudo bem. Eu só espero que alguém atualizações então o FAQ ... :-)
Daniel C. Sobral

1
Talvez você sinta falta dessa. vertx.newHttpServer.websocketHandler (_ WriteXml (html).)
angelokh

@angelokh Esse é o parâmetro de marcador de função anônimo, quinto da lista.
Daniel C. Sobral

84

Uma excelente explicação dos usos do sublinhado é Scala _ [underscore] magic .

Exemplos:

 def matchTest(x: Int): String = x match {
     case 1 => "one"
     case 2 => "two"
     case _ => "anything other than one and two"
 }

 expr match {
     case List(1,_,_) => " a list with three element and the first element is 1"
     case List(_*)  => " a list with zero or more elements "
     case Map[_,_] => " matches a map with any key type and any value type "
     case _ =>
 }

 List(1,2,3,4,5).foreach(print(_))
 // Doing the same without underscore: 
 List(1,2,3,4,5).foreach( a => print(a))

No Scala, _age de maneira semelhante ao *Java ao importar pacotes.

// Imports all the classes in the package matching
import scala.util.matching._

// Imports all the members of the object Fun (static import in Java).
import com.test.Fun._

// Imports all the members of the object Fun but renames Foo to Bar
import com.test.Fun.{ Foo => Bar , _ }

// Imports all the members except Foo. To exclude a member rename it to _
import com.test.Fun.{ Foo => _ , _ }

No Scala, um getter e um setter serão implicitamente definidos para todos os vars não privados em um objeto. O nome do getter é igual ao nome da variável e _=é adicionado ao nome do setter.

class Test {
    private var a = 0
    def age = a
    def age_=(n:Int) = {
            require(n>0)
            a = n
    }
}

Uso:

val t = new Test
t.age = 5
println(t.age)

Se você tentar atribuir uma função a uma nova variável, a função será invocada e o resultado será atribuído à variável. Essa confusão ocorre devido aos colchetes opcionais para invocação de método. Devemos usar _ após o nome da função para atribuí-lo a outra variável.

class Test {
    def fun = {
        // Some code
    }
    val funLike = fun _
}

2
Essa é uma boa explicação, mas ela nem tem todas elas. Está faltando ignorados parâmetros / variáveis, juntando-se letras e sinais de pontuação, tipos existenciais, tipos maior kinded
Owen

em sua List(1,2,3,4,5).foreach(print(_))é muito mais legível apenas para fazer List(1,2,3,4,5).foreach(print), você nem precisa realmente o sublinhado em tudo, mas eu acho que é apenas uma questão de estilo
café eléctrica

1
que tal "_" funciona como espaço reservado em Coleções com a função .map, .flatten, .toList ...... Em algum momento, isso me deixa mal entendido. :(
m0z4rt 21/09/2015

34

Há um uso que posso ver que todos aqui parecem ter esquecido de listar ...

Em vez de fazer isso:

List("foo", "bar", "baz").map(n => n.toUpperCase())

Você pode simplesmente fazer isso:

List("foo", "bar", "baz").map(_.toUpperCase())

então _ aqui atua como um espaço para nome de todas as funções disponíveis?
Crt

2
@Crt não, ele age como um atalho paran => n
café eléctrica

2
não é esta a sintaxe do espaço reservado mencionada nas duas principais respostas?
Joelb 26/09/19

13

Aqui estão mais alguns exemplos onde _é usado:

val nums = List(1,2,3,4,5,6,7,8,9,10)

nums filter (_ % 2 == 0)

nums reduce (_ + _)

nums.exists(_ > 5)

nums.takeWhile(_ < 8)

Nos exemplos acima, um sublinhado representa um elemento na lista (para reduzir o primeiro sublinhado representa o acumulador)


11

Além dos usos que JAiro mencionou, eu gosto deste:

def getConnectionProps = {
    ( Config.getHost, Config.getPort, Config.getSommElse, Config.getSommElsePartTwo )
}

Se alguém precisar de todas as propriedades da conexão, ele pode fazer:

val ( host, port, sommEsle, someElsePartTwo ) = getConnectionProps

Se você precisar de apenas um host e uma porta, poderá:

val ( host, port, _, _ ) = getConnectionProps

0

Há um exemplo específico que "_" seja usado:

  type StringMatcher = String => (String => Boolean)

  def starts: StringMatcher = (prefix:String) => _ startsWith prefix

pode ser igual a:

  def starts: StringMatcher = (prefix:String) => (s)=>s startsWith prefix

A aplicação de "_" em alguns cenários será automaticamente convertida em "(x $ n) => x $ n"


sentir exemplo de todos é um elemento de iteração, penso que este é mais como um açúcar sintaxe de baixo nível, a referida conversão lambda concisa
Ke.Steve
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.