Scala構文には多くのシンボルがあります。これらの種類の名前は検索エンジンを使用して見つけるのが難しいため、それらの包括的なリストが役立つでしょう。
Scalaのすべてのシンボルとは何ですか?それぞれのシンボルは何をしますか?
具体的には、私が知っているしたいのですが->
、||=
、++=
、<=
、_._
、::
、と:+=
。
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
これらのメソッドのほとんどの正確な意味は、それらを定義しているクラスによって異なります。たとえば、<=
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
型のオブジェクトかかりA
、A
同じ方法に無制限型パラメータです。
したがって、多くのシンボルは単にクラスのメソッドです。たとえば、
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.
val c = ex(2)
代わりにval ex(c) = 2
?
val ex(c) = 2
。
Scalaと他の言語の1つの(良い、IMO)違いは、メソッドにほとんどすべての文字で名前を付けることができることです。
列挙するのは「句読点」ではなく、単純で単純なメソッドであり、そのため、それらの動作はオブジェクトごとに異なります(いくつかの規則があります)。
たとえば、ScaladocのドキュメントでListを確認すると、ここで言及したいくつかのメソッドが表示されます。
覚えておくべきいくつかの事柄:
またはの例のように、ほとんどの場合、A operator+equal B
組み合わせはに変換されます。A = A operator B
||=
++=
で終わるメソッド:
は正しい連想です。つまり、これA :: B
は実際にB.::(A)
です。
Scalaのドキュメントを参照すると、ほとんどの答えを見つけることができます。ここに参照を保持すると、作業が重複し、すぐに遅れてしまいます:)
いくつかの基準に従って最初にそれらをグループ化できます。この投稿では、アンダースコア文字と右矢印について説明します。
_._
ピリオドが含まれています。Scalaのピリオドは常にメソッド呼び出しを示します。つまり、ピリオドの左側にはレシーバーがあり、右側にはメッセージ(メソッド名)があります。今_
はScalaの特別なシンボルです。それに関するいくつかの投稿があります。たとえば、このブログエントリはすべてのユースケースです。これは無名関数のショートカットです。つまり、1つの引数を取り、そのメソッドを呼び出す関数のショートカットです_
。現在、これ_
は有効なメソッドではないので、ほとんどの場合、関数の引数で_._1
メソッド_._1
を呼び出す、つまり同様のメソッドが表示されていました。_1
to _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)
map
Map のメソッドは、キーと値のタプルを関数の引数に渡します。値(文字列)のみに関心があるため_2
、タプルのメソッドを使用して値を抽出します。
->
方法が、あなたの文章を「だからどちらかの書き込みの結果には差がない(1, "Eins")
か、1 -> "Eins"
私は構文とその使用方法を理解する助けました」。
::
別の存在であるの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"
}
抽出プログラムの詳細については、この投稿をご覧ください。
<=
あなたがそれを「読む」のと同じです:「以下」です。したがって、それは<
(次より小さい?)、>
(次より大きい?)、==
(次と等しい?)、!=
(次と等しくない?)、<=
(次より小さいかまたは等しい?)、および>=
(次より大きい)のリストの数学演算子ですまたは等しい?)
このことは、してはならない混乱と=>
の一種である二重右矢印関数の本体から引数リストを分離するために、パターンマッチング(における試験条件分離するために使用する、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"
}
他の優れた答えに追加するだけです。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 )( _+_ )
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)
true | { println( "Icke" ); true }
。true || { println( "Icke" ); true }
⇒ 印字しません!