Quali sono tutti gli usi di un trattino basso in Scala?


Risposte:


576

Quelle a cui riesco a pensare sono

Tipi esistenziali

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

Parametri di tipo più elevati

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

Variabili ignorate

val _ = 5

Parametri ignorati

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

Nomi ignorati dei tipi di sé

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

Motivi jolly

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

Pattern jolly nelle interpolazioni

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

Carattere jolly di sequenza nei modelli

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

Importazioni di caratteri jolly

import java.util._

Nascondere le importazioni

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

Unire lettere agli operatori

def bang_!(x: Int) = 5

Operatori di assegnazione

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

Sintassi segnaposto

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

Valori del metodo

List(1, 2, 3) foreach println _

Conversione dei parametri di chiamata per nome in funzioni

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

Inizializzatore predefinito

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

Potrebbero essercene altri che ho dimenticato!


Esempio che mostra perché foo(_)e foo _sono diversi:

Questo esempio viene da 0__ :

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

  val set: Set[_ => Unit]

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

Nel primo caso, process _rappresenta un metodo; Scala prende il metodo polimorfico e tenta di renderlo monomorfo compilando il parametro type, ma si rende conto che non esiste un tipo che può essere compilato per Adare il tipo (_ => Unit) => ?(Existential _non è un tipo).

Nel secondo caso, process(_)è una lambda; quando si scrive un lambda senza un tipo di argomento esplicito, Scala deduce il tipo dall'argomento che si foreachaspetta ed _ => Unit è un tipo (mentre semplicemente _non lo è), quindi può essere sostituito e dedotto.

Questo potrebbe essere il gotcha più difficile che abbia mai incontrato.

Si noti che questo esempio viene compilato in 2.13. Ignoralo come se fosse stato assegnato al carattere di sottolineatura.


4
Penso che ce ne siano due o tre che rientrano tutti nell'uso di sottolineatura nella corrispondenza dei motivi, ma +1 per unire le lettere alla punteggiatura! :-)
Daniel C. Sobral,

22
val x: Any = _
Giovanni Botta,

2
@Owen Non credo che println _ sia una funzione parzialmente applicata. È un altro esempio di sintassi segnaposto giusto? La mappa dei significati (_ + 2) si espande in qualcosa di simile alla mappa (x => x + 2) proprio come pritnln (_) si espande in qualcosa di simile alla mappa (x => println (x))
Andrew Cassidy,

7
@AndrewCassidy In realtà println _e println(_)sono diversi. Puoi vederlo per esempio nel fatto che gestiscono i tipi esistenziali e polimorfici in modo leggermente diverso. Ne verrà fuori un esempio tra poco.
Owen,

3
@AndrewCassidy OK Ho aggiunto un esempio.
Owen,

179

Da (la mia voce) nelle FAQ , che certamente non garantisco di essere completo (ho aggiunto due voci solo due giorni fa):

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

Anche questo fa parte di questa domanda .


2
Può essere che tu possa aggiungere var i: Int = _o il caso speciale di pattern matching val (a, _) = (1, 2)o il caso speciale di scartato valfor (_ <- 1 to 10) doIt()
huynhjl

1
E def f: T; def f_=(t: T)combo per la creazione di un membro f mutabile.
huynhjl,

La corrispondenza del modello è già coperta e _sui nomi dei metodi è barare. Ma bene. Spero solo che qualcun altro aggiorni le FAQ ... :-)
Daniel C. Sobral il

1
Forse ti manca questo. vertx.newHttpServer.websocketHandler (_. writeXml (html))
angelokh

@angelokh Questo è il parametro segnaposto della funzione anonima, quinto in basso nell'elenco.
Daniel C. Sobral,

84

Un'ottima spiegazione degli usi del trattino basso è la magia di Scala _ [trattino basso] .

Esempi:

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

In Scala, si _comporta in modo simile a *Java durante l'importazione dei pacchetti.

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

In Scala, un getter e un setter saranno implicitamente definiti per tutti i var non privati ​​in un oggetto. Il nome getter è uguale al nome della variabile e _=viene aggiunto per il nome 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)

Se si tenta di assegnare una funzione a una nuova variabile, la funzione verrà invocata e il risultato verrà assegnato alla variabile. Questa confusione si verifica a causa delle parentesi graffe opzionali per l'invocazione del metodo. Dovremmo usare _ dopo il nome della funzione per assegnarlo a un'altra variabile.

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

2
Questa è una buona spiegazione, ma non li ha nemmeno tutti. Manca parametri / variabili ignorati, unendo lettere e punteggiatura, tipi esistenziali, tipi di tipo superiore
Owen,

nel tuo List(1,2,3,4,5).foreach(print(_))è molto più leggibile da fare List(1,2,3,4,5).foreach(print), non hai nemmeno bisogno del carattere di sottolineatura, ma immagino che sia solo una questione di stile
Electric Coffee

1
che ne dici di "_" funziona come segnaposto in Collezioni con la funzione .map, .flatten, .toList ...... A volte, mi fa capire male. :(
m0z4rt,

34

C'è un utilizzo che posso vedere tutti qui sembra aver dimenticato di elencare ...

Invece di fare questo:

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

Puoi semplicemente farlo:

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

quindi _ qui funge da spazio dei nomi di tutte le funzioni disponibili?
Crt

2
@Crt no, funge da scorciatoia pern => n
Electric Coffee il

2
non è questa la sintassi segnaposto menzionata nelle prime due risposte?
Joelb,

13

Ecco alcuni altri esempi in cui _viene utilizzato:

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)

In tutti gli esempi precedenti, un carattere di sottolineatura rappresenta un elemento nell'elenco (per ridurre il primo carattere di sottolineatura rappresenta l'accumulatore)


11

Oltre agli usi citati da JAiro, mi piace questo:

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

Se qualcuno ha bisogno di tutte le proprietà di connessione, può fare:

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

Se hai bisogno solo di un host e una porta, puoi fare:

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

0

C'è un esempio specifico che utilizza "_":

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

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

può essere uguale a:

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

L'applicazione di "_" in alcuni scenari verrà automaticamente convertita in "(x $ n) => x $ n"


sento che l'esempio di tutti è un elemento di iterazione, penso che sia più simile a uno zucchero di sintassi di basso livello, ha detto la conversione concisa lambda
Ke.Steve
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.