¿Cuáles son todos los usos de un guión bajo en Scala?

Respuestas:

576

Los que puedo pensar son

Tipos existenciales

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

Parámetros de tipo kinded más altos

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

Variables ignoradas

val _ = 5

Parámetros ignorados

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

Nombres ignorados de tipos propios

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

Patrones comodín

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

Patrones comodín en interpolaciones

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

Comodín de secuencia en patrones

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

Importaciones de comodines

import java.util._

Ocultar importaciones

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

Unir cartas a operadores

def bang_!(x: Int) = 5

Operadores de Asignación

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

Sintaxis de marcador de posición

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

Valores del método

List(1, 2, 3) foreach println _

Convertir parámetros de llamada por nombre en funciones

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

Inicializador predeterminado

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

¡Puede haber otros que he olvidado!


Ejemplo que muestra por qué foo(_)y foo _son diferentes:

Este ejemplo proviene de 0__ :

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

  val set: Set[_ => Unit]

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

En el primer caso, process _representa un método; Scala toma el método polimórfico e intenta hacerlo monomórfico al completar el parámetro de tipo, pero se da cuenta de que no hay ningún tipo que se pueda completar para Aque proporcione el tipo (_ => Unit) => ?(Existencial _no es un tipo).

En el segundo caso, process(_)es una lambda; al escribir una lambda sin tipo de argumento explícito, Scala infiere el tipo del argumento que foreachespera, y _ => Unit es un tipo (mientras que simplemente _no lo es), por lo que puede ser sustituido e inferido.

Este bien puede ser el truco más complicado en Scala que he encontrado.

Tenga en cuenta que este ejemplo se compila en 2.13. Ignórelo como si fuera asignado para subrayar.

Owen
fuente
44
¡Creo que hay dos o tres que se ajustan al uso de subrayado en la coincidencia de patrones, pero +1 para unir letras a signos de puntuación! :-)
Daniel C. Sobral
22
val x: Any = _
Giovanni Botta
2
@Owen No creo que println _ sea una función parcialmente aplicada. Es otro ejemplo de sintaxis de marcador de posición ¿verdad? El mapa de significado (_ + 2) se expande a algo similar al mapa (x => x + 2) de la misma manera que pritnln (_) se expande a algo similar al mapa (x => println (x))
Andrew Cassidy el
77
@AndrewCassidy En realidad println _y println(_)son diferentes. Puede ver esto, por ejemplo, en que manejan tipos existenciales y polimórficos de manera ligeramente diferente. Veremos un ejemplo en un momento.
Owen
3
@ AndrewCassidy OK, he agregado un ejemplo.
Owen
179

De (mi entrada) en las Preguntas frecuentes , que ciertamente no garantizo que esté completa (agregué dos entradas hace solo dos días):

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+)

Esto también es parte de esta pregunta .

Daniel C. Sobral
fuente
2
Puede agregar var i: Int = _o el caso especial de coincidencia de patrones val (a, _) = (1, 2)o el caso especial de val descartadofor (_ <- 1 to 10) doIt()
huynhjl
1
Y def f: T; def f_=(t: T)combo para crear un miembro f mutable.
huynhjl
La coincidencia de patrones ya está cubierta, y los _nombres de los métodos son trampas. Pero bueno, está bien. Solo espero que alguien más actualice las preguntas frecuentes ... :-)
Daniel C. Sobral
1
Quizás te pierdas este. vertx.newHttpServer.websocketHandler (_. writeXml (html))
angelokh
@angelokh Ese es el parámetro de marcador de posición de función anónima, quinto en la lista.
Daniel C. Sobral
84

Una excelente explicación de los usos del guión bajo es la magia Scala _ [guión bajo] .

Ejemplos:

 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))

En Scala, _actúa de forma similar a *Java al importar paquetes.

// 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 => _ , _ }

En Scala, un getter y setter se definirán implícitamente para todos los vars no privados en un objeto. El nombre del captador es el mismo que el nombre de la variable y _=se agrega para el nombre del definidor.

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)

Si intenta asignar una función a una nueva variable, la función se invocará y el resultado se asignará a la variable. Esta confusión ocurre debido a las llaves opcionales para la invocación del método. Deberíamos usar _ después del nombre de la función para asignarlo a otra variable.

class Test {
    def fun = {
        // Some code
    }
    val funLike = fun _
}
JAiro
fuente
2
Esa es una buena explicación, pero ni siquiera las tiene todas. Faltan parámetros / variables ignorados, unir letras y signos de puntuación, tipos existenciales, tipos de tipo superior
Owen
en tu List(1,2,3,4,5).foreach(print(_))es mucho más fácil de hacer List(1,2,3,4,5).foreach(print), ni siquiera necesitas el guión bajo en absoluto, pero supongo que eso es solo una cuestión de estilo
Electric Coffee
1
¿qué tal "_" funciona como marcador de posición en Colecciones con la función .map, .flatten, .toList ...... En algún momento, me hace entender mal. :(
m0z4rt
34

Hay un uso que puedo ver que todos aquí parecen haber olvidado enumerar ...

En lugar de hacer esto:

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

Podrías simplemente hacer esto:

List("foo", "bar", "baz").map(_.toUpperCase())
Cafe electrico
fuente
entonces _ aquí actúa como un espacio de nombres de todas las funciones disponibles?
Crt
2
@Crt no, actúa como una abreviatura den => n
Electric Coffee
2
¿No es esta la sintaxis de marcador de posición mencionada en las dos respuestas principales?
joelb
13

Aquí hay algunos ejemplos más donde _se usa:

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)

En todos los ejemplos anteriores, un guión bajo representa un elemento en la lista (para reducir, el primer guión bajo representa el acumulador)

swaraj patil
fuente
11

Además de los usos que JAiro mencionó, me gusta este:

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

Si alguien necesita todas las propiedades de conexión, puede hacer:

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

Si solo necesita un host y un puerto, puede hacer:

val ( host, port, _, _ ) = getConnectionProps
Tolitius
fuente
0

Hay un ejemplo específico de que se usará "_":

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

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

puede ser igual a:

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

La aplicación de "_" en algunos escenarios se convertirá automáticamente en "(x $ n) => x $ n"

Ke Steve
fuente
sentir el ejemplo de todo el mundo es un elemento de la iteración, creo que esto es más como un azúcar sintaxis de bajo nivel, dicha conversión lambda concisa
Ke.Steve