1. ホーム
  2. スカラ

[解決済み】Scalaの記号演算子にはどんな意味があるの?

2022-03-24 12:34:56

質問

Scalaの文法にはたくさんのシンボルがあります。この種の名称は検索エンジンでは見つけにくいので、包括的なリストがあると便利です。

Scalaの全シンボル、そしてそれぞれの役割は何ですか?

特に知りたいのは -> , ||= , ++= , <= , _._ , :: および :+= .

解決方法は?

私は、教えるために、演算子を、次のように分けています。 4つのカテゴリー :

  • キーワード/予約記号
  • 自動的にインポートされるメソッド
  • 共通メソッド
  • 構文シュガー/コンポジション

幸いなことに、ほとんどのカテゴリーが出題されています。

->    // Automatically imported method
||=   // Syntactic sugar
++=   // Syntactic sugar/composition or common method
<=    // Common method
_._   // Typo, though it's probably based on Keyword/composition
::    // Common method
:+=   // Common method

これらのメソッドの正確な意味は、それらを定義しているクラスに依存します。例えば <= について Int というのは 以下であること。 . 1つ目は -> 以下、その例を挙げる。 :: に定義されているメソッドだと思われます。 List (ただし 可能性がある は同じ名前のオブジェクトである)、そして :+= は、おそらく様々な Buffer クラスがあります。

では、見てみましょう。

キーワード/予約記号

Scalaには特別なシンボルがいくつかあります。そのうちの2つは適切なキーワードとみなされ、他のものは単に"reserved"と呼ばれます。それらは以下の通りです。

// Keywords
<-  // Used on for-comprehensions, to separate pattern from generator
=>  // Used for function types, function literals and import renaming

// Reserved
( )        // Delimit expressions and parameters
[ ]        // Delimit type parameters
{ }        // Delimit blocks
.          // Method call and path separator
// /* */   // Comments
#          // Used in type notations
:          // Type ascription or context bounds
<: >: <%   // Upper, lower and view bounds
<? <!      // Start token for various XML elements
" """      // Strings
'          // Indicate symbols and characters
@          // Annotations and variable binding on pattern matching
`          // Denote constant or enable arbitrary identifiers
,          // Parameter separator
;          // Statement separator
_*         // vararg expansion
_          // Many different meanings

これらはすべて 言語の一部 のように、言語を適切に説明するあらゆるテキストで見つけることができます。 Scala仕様書 (PDF)そのものです。

最後のアンダースコアは、非常に広く使われ、さまざまな意味を持つので、特別に説明する必要があります。以下はその例である。

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

おそらく他の意味を忘れているのでしょうけど。

自動的にインポートされるメソッド

ですから、もしあなたが探しているシンボルが上のリストになかったら、それはメソッドか、その一部であるはずです。しかし、しばしば、あるシンボルが見えても、そのクラスのドキュメントにそのメソッドが載っていないことがあります。このような場合は、1つまたは複数のメソッドと他の何かの合成を見ているか、メソッドがスコープにインポートされているか、インポートされた暗黙の変換によって利用可能であるかのいずれかです。

これらは はまだ見つけることができます ScalaDoc どこにあるのかを知っていればいいのです。そうでない場合は インデックス (現在2.9.1では壊れていますが、ナイトリーでは利用可能です)。

すべてのScalaコードには3つの自動インポートがあります。

// Not necessarily in this order
import _root_.java.lang._      // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._

最初の2つはクラスとシングルトンオブジェクトだけを利用できるようにしたものです。3番目のものは、すべての暗黙の変換とインポートメソッドを含んでいます。 Predef はオブジェクトそのものである。

内部を見る Predef は、いくつかの記号を素早く表示します。

class <:<
class =:=
object <%<
object =:=

その他のシンボルは 暗黙の変換 . でタグ付けされたメソッドを見てください。 implicit で、パラメータとして、そのメソッドを受け取る型のオブジェクトを受け取ります。例えば

"a" -> 1  // Look for an implicit from String, AnyRef, Any or type parameter

上記の場合 -> は、クラス ArrowAssoc メソッドを通じて any2ArrowAssoc という型のオブジェクトを受け取り A ここで A は同じメソッドに対する非束縛型パラメータである。

共通メソッド

つまり、多くのシンボルは、単にクラスに対するメソッドなのです。たとえば、次のようにすると

List(1, 2) ++ List(3, 4)

というメソッドを見つけることができます。 ++ の ScalaDoc の右側にある。 リスト . ただし、メソッドを検索するときに注意しなければならない慣例があります。コロンで終わるメソッド ( : をバインドします。 を右へ の代わりに、左を使用します。言い換えれば、上記のメソッド呼び出しが等価であるのに対して

List(1, 2).++(List(3, 4))

もし私が、その代わりに 1 :: List(2, 3) と等価になります。

List(2, 3).::(1)

そこで、見つかった型を見る必要があります 右側 コロンで終わるメソッドを探すとき 例えば、考えてみましょう。

1 +: List(2, 3) :+ 4

最初のメソッド( +: ) は右側にバインドされます。 List . 2番目のメソッド ( :+ は通常のメソッドであり、左側にバインドされます。 List .

構文糖・合成

そこで、メソッドを隠す可能性のある構文シュガーをいくつか紹介します。

class Example(arr: Array[Int] = Array.fill(5)(0)) {
  def apply(n: Int) = arr(n)
  def update(n: Int, v: Int) = arr(n) = v
  def a = arr(0); def a_=(v: Int) = arr(0) = v
  def b = arr(1); def b_=(v: Int) = arr(1) = v
  def c = arr(2); def c_=(v: Int) = arr(2) = v
  def d = arr(3); def d_=(v: Int) = arr(3) = v
  def e = arr(4); def e_=(v: Int) = arr(4) = v
  def +(v: Int) = new Example(arr map (_ + v))
  def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None
}

val Ex = new Example // or var for the last example
println(Ex(0))  // calls apply(0)
Ex(0) = 2       // calls update(0, 2)
Ex.b = 3        // calls b_=(3)
// This requires Ex to be a "val"
val Ex(c) = 2   // calls unapply(2) and assigns result to c
// This requires Ex to be a "var"
Ex += 1         // substituted for Ex = Ex + 1

最後の1つは、興味深いことに 任意の 記号的なメソッドを組み合わせて、そのように割り当てのようなメソッドを形成することができます。

もちろん、コードに登場する組み合わせもさまざまです。

(_+_) // An expression, or parameter, that is an anonymous function with
      // two parameters, used exactly where the underscores appear, and
      // which calls the "+" method on the first parameter passing the
      // second parameter as argument.