Apa semua kegunaan garis bawah di Scala?

Jawaban:

576

Yang bisa saya pikirkan adalah

Jenis eksistensial

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

Parameter jenis yang lebih tinggi

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

Variabel yang diabaikan

val _ = 5

Parameter yang diabaikan

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

Nama tipe diri yang diabaikan

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

Pola wildcard

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

Pola wildcard dalam interpolasi

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

Urutan wildcard dalam pola

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

Impor wildcard

import java.util._

Menyembunyikan impor

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

Menggabungkan surat dengan operator

def bang_!(x: Int) = 5

Operator penugasan

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

Sintaksis placeholder

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

Nilai metode

List(1, 2, 3) foreach println _

Mengubah parameter panggilan-nama-ke fungsi

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

Penginisialisasi default

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

Mungkin ada orang lain yang saya lupa!


Contoh menunjukkan mengapa foo(_)dan foo _berbeda:

Contoh ini berasal dari 0__ :

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

  val set: Set[_ => Unit]

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

Dalam kasus pertama, process _merupakan metode; Scala mengambil metode polimorfik dan berupaya membuatnya monomorfik dengan mengisi parameter tipe, tetapi menyadari bahwa tidak ada tipe yang dapat diisi untuk Ayang akan memberikan tipe (_ => Unit) => ?(Eksistensial _bukan tipe).

Dalam kasus kedua, process(_)adalah lambda; saat menulis lambda tanpa tipe argumen eksplisit, Scala menyimpulkan tipe dari argumen yang foreachdiharapkan, dan _ => Unit merupakan tipe (padahal _sebenarnya tidak), sehingga dapat diganti dan disimpulkan.

Ini mungkin merupakan gotcha paling sulit di Scala yang pernah saya temui.

Perhatikan bahwa contoh ini dikompilasi dalam 2.13. Abaikan saja seolah-olah ditugaskan ke garis bawah.

Owen
sumber
4
Saya pikir ada dua atau tiga yang semuanya cocok di bawah penggunaan garis bawah dalam pencocokan pola, tetapi +1 untuk menggabungkan huruf menjadi tanda baca! :-)
Daniel C. Sobral
22
val x: Any = _
Giovanni Botta
2
@Owen Saya tidak berpikir println _ adalah fungsi yang diterapkan sebagian. Ini adalah contoh lain sintaks placeholder kan? Arti peta (_ + 2) meluas ke sesuatu yang mirip dengan peta (x => x + 2) sama seperti pritnln (_) meluas ke sesuatu yang mirip dengan peta (x => println (x))
Andrew Cassidy
7
@AndrewCassidy Sebenarnya println _dan println(_)berbeda. Anda dapat melihat ini sebagai contoh karena mereka menangani tipe eksistensial dan polimorfik sedikit berbeda. Akan muncul dengan sedikit contoh.
Owen
3
@AndrewCassidy OK Saya telah menambahkan contoh.
Owen
179

Dari (entri saya) di FAQ , yang tentu saja saya tidak menjamin akan lengkap (saya menambahkan dua entri hanya dua hari yang lalu):

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

Ini juga bagian dari pertanyaan ini .

Daniel C. Sobral
sumber
2
Mungkin Anda dapat menambahkan var i: Int = _atau kasus khusus pencocokan pola val (a, _) = (1, 2)atau kasus khusus dari val dibuangfor (_ <- 1 to 10) doIt()
huynhjl
1
Dan def f: T; def f_=(t: T)kombo untuk membuat f member yang bisa berubah.
huynhjl
Pencocokan pola sudah dicakup, dan _pada nama metode curang. Tapi baiklah. Saya hanya berharap orang lain memperbarui FAQ ... :-)
Daniel C. Sobral
1
Mungkin Anda melewatkan yang satu ini. vertx.newHttpServer.websocketHandler (_. writeXml (html))
angelokh
@angelokh Itu parameter tempat penampung fungsi anonim, kelima di bawah daftar.
Daniel C. Sobral
84

Penjelasan yang bagus tentang penggunaan garis bawah adalah sihir Scala _ [garis bawah] .

Contoh:

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

Di Scala, _tindakan serupa dengan *di Jawa saat mengimpor paket.

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

Dalam Scala, pengambil dan penyetel akan secara implisit didefinisikan untuk semua vars non-pribadi dalam suatu objek. Nama pengambil sama dengan nama variabel dan _=ditambahkan untuk nama penyetel.

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

Pemakaian:

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

Jika Anda mencoba untuk menetapkan fungsi ke variabel baru, fungsi akan dipanggil dan hasilnya akan ditugaskan ke variabel. Kebingungan ini terjadi karena kawat gigi opsional untuk pemanggilan metode. Kita harus menggunakan _ setelah nama fungsi untuk menetapkannya ke variabel lain.

class Test {
    def fun = {
        // Some code
    }
    val funLike = fun _
}
Jairo
sumber
2
Itu penjelasan yang bagus, tetapi bahkan tidak semuanya. Tidak ada parameter / variabel yang diabaikan, gabungan huruf dan tanda baca, tipe eksistensial, tipe yang lebih tinggi
Owen
di Anda List(1,2,3,4,5).foreach(print(_))itu jauh lebih mudah untuk dilakukan List(1,2,3,4,5).foreach(print), Anda bahkan tidak benar-benar membutuhkan garis bawah sama sekali, tapi saya rasa itu hanya masalah gaya
Electric Coffee
1
bagaimana dengan "_" berfungsi sebagai place holder di Collections dengan fungsi .map, .flatten, .toList ...... Kadang-kadang, itu membuat saya salah paham. :(
m0z4rt
34

Ada satu penggunaan yang bisa saya lihat semua orang di sini sepertinya lupa daftar ...

Daripada melakukan ini:

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

Anda bisa melakukannya:

List("foo", "bar", "baz").map(_.toUpperCase())
Kopi Listrik
sumber
jadi _ di sini bertindak sebagai namespace dari semua fungsi yang tersedia?
Crt
2
@Crt no, ini bertindak sebagai singkatan untukn => n
Electric Coffee
2
bukankah ini sintaks placeholder yang disebutkan dalam dua jawaban teratas?
joelb
13

Berikut adalah beberapa contoh di mana _digunakan:

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)

Dalam semua contoh di atas satu garis bawah mewakili elemen dalam daftar (untuk mengurangi garis bawah pertama mewakili akumulator)

swaraj patil
sumber
11

Selain penggunaan yang disebutkan JAiro, saya suka yang ini:

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

Jika seseorang membutuhkan semua properti koneksi, ia dapat melakukan:

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

Jika Anda hanya membutuhkan host dan port, Anda dapat melakukan:

val ( host, port, _, _ ) = getConnectionProps
tolitius
sumber
0

Ada contoh spesifik yang digunakan "_":

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

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

mungkin sama dengan:

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

Menerapkan “_” dalam beberapa skenario akan secara otomatis dikonversi menjadi “(x $ n) => x $ n”

Ke.Steve
sumber
merasa contoh semua orang adalah elemen iterasi, saya pikir ini lebih seperti gula sintaksis tingkat rendah, kata lambda konversi singkat
Ke.