Scalaのすべての記号演算子はどういう意味ですか?


402

Scala構文には多くのシンボルがあります。これらの種類の名前は検索エンジンを使用して見つけるのが難しいため、それらの包括的なリストが役立つでしょう。

Scalaのすべてのシンボルとは何ですか?それぞれのシンボルは何をしますか?

具体的には、私が知っているしたいのですが->||=++=<=_._::、と:+=


4
階段の第1版のインデックス、>> artima.com/pins1ed/book-index.html#indexanchor
Gene T

2
関連:英数字VSオペレータの文字:stackoverflow.com/questions/7656937/...
ルイジPlinge

1
また、scalexや階段本で見つけることができない「演算子」(ほとんどがメソッドで、いくつかのクラス名がインフィックスで使用されている)がある場合(例:「!!」)、おそらくソースはakka、scalazのscaladocsです。とsbt
Gene T

infixで使用されているクラス名の例(ドイツ語)>> raichoo.blogspot.com/2010/06/spass-mit-scala-infixtypen.html
Gene T

検索エンジンによるフィルタリングの問題に関して、symbolound.comも良い代替案です
Patrick Refondini 2014年

回答:


526

教えるために、オペレーターを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

これらのメソッドのほとんどの正確な意味は、それらを定義しているクラスによって異なります。たとえば、<=on Int「以下」を意味します。最初の->例は、以下の例のようになります。::おそらくList(同じ名前のオブジェクトである可能性があります)で:+=定義されているメソッドであり、おそらくさまざまなBufferクラスで定義されているメソッドです。

だから、それらを見てみましょう。

キーワード/予約済みシンボル

Scalaには特別なシンボルがいくつかあります。それらの2つは適切なキーワードと見なされますが、その他は単に「予約済み」です。彼らです:

// 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そのreceiveでタグ付けされたメソッドをパラメーターとして、メソッドを受け取るタイプのオブジェクトを見てください。例えば:

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

上記の場合では、->クラスで定義されているArrowAssoc方法を介してany2ArrowAssoc型のオブジェクトかかりAA同じ方法に無制限型パラメータです。

一般的な方法

したがって、多くのシンボルは単にクラスのメソッドです。たとえば、

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

このメソッド++は、ScalaDoc for Listにあります。ただし、メソッドを検索するときに注意する必要がある規則が1つあります。コロン(:)で終わるメソッドは、左ではなくにバインドます。つまり、上記のメソッド呼び出しは以下と同等です。

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

最後の方法は興味深いものです。これは、任意のシンボリックメソッドを組み合わせて割り当てのようなメソッドをそのように形成できるためです。

そしてもちろん、コードに表示できるさまざまな組み合わせがあります。

(_+_) // 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.

1
もしかしてval c = ex(2)代わりにval ex(c) = 2
Mike Stay

3
@MikeStayいいえ、意味はありませんでしたval ex(c) = 2
ダニエルC.ソブラル2014年

ああ、それはパターンマッチング構文を使用しています。ありがとう。
マイクステイ

=>また、:とyのように入力する場合の間に使用すると、「名前で呼び出す」ステータスが付与されます。=> Int '
スティーブンW.ライト

1
たぶん:/と:\も直感的でない演算子に言及すべきでしょう。したがって、map.foldLeft(initialVal)は(initialVal:/ map)と同じです-代わりに:\はfoldRightです。
MT氏

24

Scalaと他の言語の1つの(良い、IMO)違いは、メソッドにほとんどすべての文字で名前を付けることができることです。

列挙するのは「句読点」ではなく、単純で単純なメソッドであり、そのため、それらの動作はオブジェクトごとに異なります(いくつかの規則があります)。

たとえば、ScaladocのドキュメントでListを確認すると、ここで言及したいくつかのメソッドが表示されます。

覚えておくべきいくつかの事柄:

  • またはの例のように、ほとんどの場合、A operator+equal B組み合わせはに変換されます。A = A operator B||=++=

  • で終わるメソッド:は正しい連想です。つまり、これA :: Bは実際にB.::(A)です。

Scalaのドキュメントを参照すると、ほとんどの答えを見つけることができます。ここに参照を保持すると、作業が重複し、すぐに遅れてしまいます:)


21

いくつかの基準に従って最初にそれらをグループ化できます。この投稿では、アンダースコア文字と右矢印について説明します。

_._ピリオドが含まれています。Scalaのピリオドは常にメソッド呼び出しを示します。つまり、ピリオドの左側にはレシーバーがあり、右側にはメッセージ(メソッド名)があります。今_はScalaの特別なシンボルです。それに関するいくつかの投稿があります。たとえば、このブログエントリはすべてのユースケースです。これは無名関数のショートカットです。つまり、1つの引数を取り、そのメソッドを呼び出す関数のショートカットです_。現在、これ_は有効なメソッドではないので、ほとんどの場合、関数の引数で_._1メソッド_._1を呼び出す、つまり同様のメソッドが表示されていました。_1to _22は、タプルの特定の要素を抽出するタプルのメソッドです。例:

val tup = ("Hallo", 33)
tup._1 // extracts "Hallo"
tup._2 // extracts 33

次に、関数アプリケーションのショートカットのユースケースを想定します。整数を文字列にマップするマップが与えられます:

val coll = Map(1 -> "Eins", 2 -> "Zwei", 3 -> "Drei")

おっと、もう一回奇妙な句読点が発生しています。右側の矢印に似たハイフンと大なり記号は、を生成する演算子Tuple2です。つまり、(1, "Eins")またはのどちらを書き込んでも結果は変わりません1 -> "Eins"。特に、マップの例のようなタプルのリストでは、後者の方が読みやすいだけです。これ->は魔法ではありません。他のいくつかの演算子と同様に、スコープ内のオブジェクトですべての暗黙的な変換を行うことができるため、利用できますscala.Predef。ここで行われる変換は

implicit def any2ArrowAssoc [A] (x: A): ArrowAssoc[A] 

を作成ArrowAssocする->メソッドはどこにありますかTuple2。したがって1 -> "Eins"、実際の呼び出しPredef.any2ArrowAssoc(1).->("Eins")です。OK。次に、アンダースコア文字を使用して元の質問に戻ります。

// lets create a sequence from the map by returning the
// values in reverse.
coll.map(_._2.reverse) // yields List(sniE, iewZ, ierD)

ここでの下線は、次の同等のコードを短くします。

coll.map(tup => tup._2.reverse)

mapMap のメソッドは、キーと値のタプルを関数の引数に渡します。値(文字列)のみに関心があるため_2、タプルのメソッドを使用して値を抽出します。


私は理解しようと悩みを抱えていた1 ->方法が、あなたの文章を「だからどちらかの書き込みの結果には差がない(1, "Eins")か、1 -> "Eins"私は構文とその使用方法を理解する助けました」。
Jesse Webb 2013年

ブログエントリのリンクが
機能していません

15

ダニエルと0__の見事な答えに加えて、Scala は一部のシンボルのUnicodeアナログを理解するので、代わりに

for (n <- 1 to 10) n % 2 match {
  case 0 => println("even")
  case 1 => println("odd")
}

書くかもしれない

for (n ← 1 to 10) n % 2 match {
  case 0 ⇒ println("even")
  case 1 ⇒ println("odd")
}

10

::別の存在であるのStackOverflowのカバーエントリ::ケースを。要するに、それは、ヘッド要素とテールリストをLists考える」ことによって構築するために使用されます。これは、cons'edリストを表すクラスであり、エクストラクターとして使用できますが、最も一般的には、リストのメソッドです。Pablo Fernandezが指摘しているように、コロンで終了しているため、これは右結合です。つまり、メソッド呼び出しの受信者は右側にあり、引数は演算子の左側にあります。あなたはエレガントとコンスを表現することができますこの方法先頭に追加し、既存のリストに新しいヘッド素子:

val x = 2 :: 3 :: Nil  // same result as List(2, 3)
val y = 1 :: x         // yields List(1, 2, 3)

これは

val x = Nil.::(3).::(2) // successively prepend 3 and 2 to an empty list
val y = x.::(1)         // then prepend 1

抽出オブジェクトとしての使用は次のとおりです。

def extract(l: List[Int]) = l match {
   case Nil          => "empty"
   case head :: Nil  => "exactly one element (" + head + ")"
   case head :: tail => "more than one element"
}

extract(Nil)          // yields "empty"
extract(List(1))      // yields "exactly one element (1)"
extract(List(2, 3))   // yields "more than one element"

これはここでは演算子のように見えますが、これは実際にはもう1つの(より読みやすい)書き方です

def extract2(l: List[Int]) = l match {
   case Nil            => "empty"
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   case ::(head, tail) => "more than one element"
}

抽出プログラムの詳細については、この投稿をご覧ください。


9

<=あなたがそれを「読む」のと同じです:「以下」です。したがって、それは<(次より小さい?)、>(次より大きい?)、==(次と等しい?)、!=(次と等しくない?)、<=(次より小さいかまたは等しい?)、および>=(次より大きい)のリストの数学演算子ですまたは等しい?)

このことは、してはならない混乱=>の一種である二重右矢印関数の本体から引数リストを分離するために、パターンマッチング(における試験条件分離するために使用する、case一致が発生したときに実行本体からブロック) 。この例は、前の2つの回答で確認できます。まず、関数の使用:

coll.map(tup => tup._2.reverse)

タイプが省略されているため、これはすでに省略されています。フォロー機能は

// function arguments         function body
(tup: Tuple2[Int, String]) => tup._2.reverse

そしてパターンマッチングの使用:

def extract2(l: List[Int]) = l match {
   // if l matches Nil    return "empty"
   case Nil            => "empty"
   // etc.
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   // etc.
   case ::(head, tail) => "more than one element"
}

4
この混乱を回避するために、右二重矢印(\ U21D2)、単一の右「マップ」矢印(\ U2192)、および左の単一「入力」矢印(\ U2190)にUnicode文字を使い始めることにしました。Scalaはこれをサポートしていますが、しばらく試してみるまでは少し懐疑的でした。これらのコードポイントをシステムの便利なキーの組み合わせにバインドする方法を調べてください。OS Xでは本当に簡単でし
Connor Doyle

5

大規模なscalaプロジェクトを理解するには、現代のIDEが重要だと思います。これらの演算子もメソッドなので、intellijのアイデアでは、controlキーを押しながらクリックするか、controlキーを押しながらbキーを押すだけで定義を定義できます。

cons演算子(::)を右クリックして制御し、「このリストの最初に要素を追加します」と言ってScala javadocに到達することができます。ユーザー定義の演算子では、見つけにくい暗黙で定義できるため、これはさらに重要になります... IDEは暗黙がどこで定義されたかを認識しています。


4

他の優れた答えに追加するだけです。Scalaは、よく批判される2つの記号演算子/:foldLeft)および:\foldRight)を提供しています。最初の演算子は右結合です。したがって、次の3つのステートメントは同等です。

( 1 to 100 ).foldLeft( 0, _+_ )
( 1 to 100 )./:( 0 )( _+_ )
( 0 /: ( 1 to 100 ) )( _+_ )

これらの3つと同様に:

( 1 to 100 ).foldRight( 0, _+_ )
( 1 to 100 ).:\( 0 )( _+_ )
( ( 1 to 100 ) :\ 0 )( _+_ )

2

ScalaはJavaの算術演算子のほとんどを継承しています。これには、ビット単位のOR |(単一のパイプ文字)、ビット単位のAND &、ビット単位の排他的OR、および^論理(ブール)または||(2つのパイプ文字)および論理ANDが含まれ&&ます。興味深いことに、で単一文字演算子を使用できるbooleanため、java'ish論理演算子は完全に冗長です。

true && true   // valid
true & true    // valid as well

3 & 4          // bitwise-and (011 & 100 yields 000)
3 && 4         // not valid

別の投稿で指摘されているように、等号で終わる呼び出しは=、再割り当てによって解決されます(その名前のメソッドが存在しない場合)。

var x = 3
x += 1         // `+=` is not a method in `int`, Scala makes it `x = x + 1`

この「ダブルチェック」により、ミュータブルをイミュータブルコレクションに簡単に交換できます。

val m = collection.mutable.Set("Hallo")   // `m` a val, but holds mutable coll
var i = collection.immutable.Set("Hallo") // `i` is a var, but holds immutable coll

m += "Welt" // destructive call m.+=("Welt")
i += "Welt" // re-assignment i = i + "Welt" (creates a new immutable Set)

4
PSブール値での単一文字演算子とダブル文字演算子の使用には違いがあります。前者は熱心であり(すべての用語が評価されます)、結果のブール値がわかっている場合、後者は早期に終了しますtrue | { println( "Icke" ); true }true || { println( "Icke" ); true }⇒ 印字しませ
0__ 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.