web-dev-qa-db-ja.com

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

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

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

特に、->||=++=<=_._::、および:+=について知りたいのですが。

386
0__

私は、教えを目的として、演算子を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<=は、が「以下」を意味します。最初のもの、->、以下に例を挙げます。 ::はおそらく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 Specification (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 の上にまだ見つけることができます:あなたはそれらを探す場所を知る必要があります。あるいは失敗した場合は、 index を見てください(現在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つはクラスとシングルトンオブジェクトのみを利用可能にします。 Predef はそれ自身がオブジェクトなので、3番目のものはすべての暗黙の変換とインポートされたメソッドを含みます。

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)

List のメソッド++が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

最後の方法は、anyのシンボリックメソッドを組み合わせて代入のようなメソッドを作成できるという点で興味深いものです。

そしてもちろん、コードに現れる可能性があるさまざまな組み合わせがあります。

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

Scalaと他の言語の間の(良い、IMOの)違いはそれがあなたがほとんどどんな文字であなたの方法に名前を付けることを可能にするということです。

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

例えば、 ListのScaladocドキュメンテーション をチェックすれば、あなたがここで述べたいくつかのメソッドを見るでしょう。

留意すべき点がいくつかあります。

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

  • :で終わるメソッドは正しい連想です。これはA :: Bが実際にはB.::(A)であることを意味します。

ほとんどの答えはScalaのドキュメントを閲覧することで見つけることができます。ここで参照を維持することは努力を複製するでしょう、そしてそれはすぐに遅れるでしょう:)

23
Pablo Fernandez

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

_._はピリオドを含みます。 Scalaのピリオドは常にメソッド呼び出しを示します。ピリオドの左側にはレシーバーがあり、右側にはメッセージ(メソッド名)があります。今_はScalaでは特殊記号です。それについてはいくつかの投稿があります、例えば このブログ記事 すべてのユースケース。これは無名関数のショートカットです。つまり、引数を1つ取り、そのメソッドで_メソッドを呼び出す関数へのショートカットです。 _は有効なメソッドではありません。そのため、ほとんどの場合、_._1またはそれに類似したもの、つまり関数の引数でメソッド_._1を呼び出すことになります。 _1から_22は、Tupleの特定の要素を抽出するタプルのメソッドです。例:

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)

Mapのmapメソッドは、キーと値のタプルを関数の引数に渡します。値(文字列)だけに関心があるので、Tupleの_2メソッドでそれらを抽出します。

20
0__

Danielと0__のすばらしい答えに加えて、Scalaはシンボルのいくつかについて nicode 類推を理解しているので、その代わりに

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")
}
14
om-nom-nom

::に関しては、::の場合をカバーする Stackoverflow エントリがもう1つあります。つまり、ヘッドエレメントとテールリストを ')で結合することによってListsを構築するために使用されます。それは両方とも class で、消費されたリストを表し、抽出子として使用できますが、最も一般的にはmethod onリスト。 Pablo Fernandezが指摘しているように、コロンで終わっているので、それは右結合であり、メソッド呼び出しの受信者は右にあり、演算子の左側にある引数。そのようにして、あなたはエレガントにそのことをprepending既存のリストの新しいhead要素として表現することができます。

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 (33)"
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
0__

<=は、あなたがそれを「読む」のと同じように、「より小か等しい」です。 <(より小さいか)、>(より大きいか)、==(等しいかどうか)、!=(等しいかどうか?)、<=(それ以下かどうか)のリストの中の数学演算子です。 >=(以上か?).

これはconfused=>との組み合わせではいけません。これは二重右矢印の一種で、引数リストを本体から分離するために使用されます。そして、パターンマッチング(caseブロック)のテスト条件を、一致が発生したときに実行される本体から切り離します。あなたは私の前の二つの答えでこの例を見ることができます。まず、この関数は

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"
}
9
0__

私は現代のIDEが大規模なスカラプロジェクトを理解するのに重要だと考えています。これらの演算子もメソッドなので、intelijのアイデアでは、定義をクリックするかcontrol-bをクリックするだけです。

あなたは右クリックしてcons演算子(::)に入ることができ、「このリストの先頭に要素を追加する」と言ってscala javadocに行き着くことができます。ユーザ定義の演算子では、見つけにくい暗黙の内に定義することができるので、これはさらに重要になります...あなたのIDEは、暗黙の定義の場所を知っています。

5
nairbv

他の優れた答えを追加するだけです。 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 )( _+_ )
4
Mr MT

Scalaは Javaの算術演算子 の大部分を継承しています。これには、bitwise-or |(単一パイプ文字)、bitwise-and &、bitwise-exclusive-or ^、およびlogical(boolean)または||(2つのパイプ文字)、およびlogical-and &&が含まれます。興味深いことに、booleanでは単一文字の演算子を使うことができるので、Java風の論理演算子はまったく不要です。

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)
2
0__