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

Eché un vistazo a la lista de encuestas tomadas en scala-lang.org y noté una pregunta curiosa: ” ¿Puedes nombrar todos los usos de” _ “? “. ¿Puedes? Si es así, hágalo aquí. Ejemplos explicativos son apreciados

Los que puedo pensar son

Tipos existenciales

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

Parámetros de tipo de par superior

 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 los propios tipos

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

Patrones comodín

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

Importación de comodines

 import java.util._ 

Esconder las importaciones

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

Unir letras a la puntuación

 def bang_!(x: Int) = 5 

Operadores de Asignación

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

Placeholder syntax

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

Funciones parcialmente aplicadas

 List(1, 2, 3) foreach println _ 

Conversión de parámetros de llamada por nombre a funciones

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

¡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, el process _ representa un método; Scala toma el método polimórfico e intenta hacerlo monomórfico completando el parámetro tipo, pero se da cuenta de que no hay ningún tipo que se pueda completar para A que dará el tipo (_ => Unit) => ? (Existencial _ no es un tipo).

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

Esta bien puede ser la trampa más difícil en Scala que he encontrado.

De (mi entrada) en las preguntas frecuentes , que ciertamente no garantizo que esté completo (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 

Esto también es parte de esta pregunta .

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 * en 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 un setter se definirán implícitamente para todos los vars no privados en un objeto. El nombre getter es igual que el nombre de la variable y _= se agrega para el nombre del 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) 

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 se produce debido a las llaves opcionales para invocación de 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 _ } 

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

Usted podría simplemente hacer esto:

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

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 subrayado representa el acumulador)

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 
 import scala._ // Wild card -- all of Scala is imported import scala.{ Predef => _, _ } // Exclusion, 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 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 Please check the below link for more details https://docs.scala-lang.org/tutorials/FAQ/finding-symbols.html 

Hay un ejemplo específico de que se usa “_”:

  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 

Aplicando “_” en algunos escenarios se convertirá automáticamente a “(x $ n) => x $ n”