括弧()
と括弧{}
で関数に引数を渡すことの正式な違いは何ですか?
Scalaでのプログラミング本から得た感触は、Scalaの柔軟性が非常に高いため、自分が気に入ったものを使用する必要があることです。しかし、いくつかのケースはコンパイルされますが、他のケースはコンパイルされません。
たとえば(単なる例であり、この特定の例だけでなく、一般的なケースを説明する応答をいただければ幸いです):
val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
=>エラー:単純式の不正な開始
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
=>結構です。
これについて一度書いてみましたが、ルールがやや広まっているので、結局endめました。基本的に、あなたはそれを理解する必要があります。
おそらく、中括弧と括弧を同じ意味で使用できる場所、つまりメソッド呼び出しにパラメーターを渡す場所に集中するのが最善です。メソッドが単一のパラメーターを想定している場合にのみ、may括弧を中括弧に置き換えます。例えば:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
ただし、これらのルールをよりよく把握するには、さらに多くのことを知る必要があります。
Sprayの作成者は、コンパイルチェックを強化するため、丸括弧を推奨しています。これは、スプレーのようなDSLにとって特に重要です。括弧を使用することにより、コンパイラに1行のみを指定するよう指示しています。したがって、誤って2つ以上を与えると、文句を言うでしょう。これは中括弧では当てはまりません。たとえば、演算子をどこかで忘れると、コードがコンパイルされ、予期しない結果と非常に難しいバグが見つかる可能性があります。以下は不自然なものです(式は純粋であり、少なくとも警告を出すため)。
method {
1 +
2
3
}
method(
1 +
2
3
)
最初はコンパイルされ、2番目はerror: ')' expected but integer literal found
を返します。著者は1 + 2 + 3
を書きたかった。
既定の引数を持つマルチパラメーターメソッドについても同様であると主張できます。括弧を使用している場合、誤ってコンマを忘れてパラメーターを区切ることはできません。
冗長性についてしばしば見落とされがちな重要なメモ。 Scala style guide には右中括弧を閉じる必要があることを明確に示しているため、中括弧を使用すると必然的に冗長なコードになります。
…右中括弧は、関数の最後の行の直後の独自の行にあります。
IntelliJのような多くの自動リフォーマッタは、この再フォーマットを自動的に実行します。そのため、可能な場合は丸括弧を使用するようにしてください。
List(1,2,3) indexOf (2)
のように中置記法を使用する場合、パラメーターが1つしかない場合は括弧を省略してList(1, 2, 3) indexOf 2
として記述できます。これはドット表記の場合ではありません。
x + 2
やa => a % 2 == 0
のようなマルチトークン式である単一のパラメーターがある場合は、括弧を使用して式の境界を示す必要があることにも注意してください。
場合によっては括弧を省略できるため、タプルには((1, 2))
のように追加の括弧が必要な場合があります。また、(1, 2)
のように外側の括弧を省略できる場合もあります。これにより混乱が生じる可能性があります。
case
を含む関数/部分関数リテラルScalaには、関数および部分関数リテラルの構文があります。次のようになります。
{
case pattern if guard => statements
case pattern => statements
}
case
ステートメントを使用できる他の唯一の場所は、match
およびcatch
キーワードを使用する場合です。
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
他のコンテキストではcase
ステートメントを使用できません。したがって、case
を使用する場合は、need中括弧を使用します。何が関数と部分関数リテラルを区別するのか疑問に思っている場合、答えは次のとおりです。コンテキスト。 Scalaが関数を必要とする場合、取得する関数。部分的な関数が必要な場合、部分的な関数を取得します。両方が予想される場合、あいまいさに関するエラーが発生します。
括弧を使用して部分式を作成できます。中括弧は、コードのブロックを作成するために使用できます(これはnot関数リテラルであるため、1つのように使用しようとすることに注意してください)。コードブロックは複数のステートメントで構成され、各ステートメントはインポートステートメント、宣言、または式にすることができます。こんなふうになります:
{
import stuff._
statement ; // ; optional at the end of the line
statement ; statement // not optional here
var x = 0 // declaration
while (x < 10) { x += 1 } // stuff
(x % 5) + 1 // expression
}
( expression )
そのため、宣言、複数のステートメント、import
などが必要な場合は、中括弧が必要です。また、式はステートメントであるため、括弧は中括弧内に表示される場合があります。しかし、興味深いのは、コードブロックがalso式であるため、どこでもinside式:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
したがって、式はステートメントであり、コードのブロックは式であるため、以下はすべて有効です。
1 // literal
(1) // expression
{1} // block of code
({1}) // expression with a block of code
{(1)} // block of code with an expression
({(1)}) // you get the drift...
基本的に、他の場所で{}
を()
に、またはその逆に置き換えることはできません。例えば:
while (x < 10) { x += 1 }
これはメソッド呼び出しではないため、他の方法で記述することはできません。まあ、中括弧をcondition
の括弧の内側に入れたり、括弧を使用したりできます。 insideコードブロックの波括弧:
while ({x < 10}) { (x += 1) }
だから、これが役立つことを願っています。
ここでは、いくつかの異なるルールと推論が行われています。まず、Scalaは、パラメーターが関数の場合にブレースを推論します。 list.map(_ * 2)
では、中括弧が推測されます。これはlist.map({_ * 2})
の短い形式です。次に、Scalaを使用すると、最後のパラメーターリストの括弧をスキップできます。パラメーターリストにパラメーターが1つあり、それが関数である場合、list.foldLeft(0)(_ + _)
はlist.foldLeft(0) { _ + _ }
(またはlist.foldLeft(0)({_ + _})
を明示的に追加する場合)。
ただし、case
を追加すると、他の人が述べたように、関数ではなく部分関数を取得し、Scalaは部分関数の波括弧を推測しないため、list.map(case x => x * 2)
が勝ちます動作しませんが、list.map({case x => 2 * 2})
とlist.map { case x => x * 2 }
の両方が機能します。
中括弧と括弧の使用法を標準化するためのコミュニティからの努力があります。Scalaスタイルガイド(21ページ)を参照してください: http://www.codecommit.com/scala-style-guide .pdf
高次のメソッド呼び出しに推奨される構文は、常に中括弧を使用し、ドットをスキップすることです。
val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }
「通常の」metod呼び出しには、ドットと括弧を使用する必要があります。
val result = myInstance.foo(5, "Hello")
Scalaの中括弧に特別なことや複雑なことはないと思います。 Scalaでそれらの複雑な使用法を習得するには、いくつかの簡単なことを念頭に置いてください。
上記の3つのルールごとに、いくつかの例を説明します。
val tupleList = List[(String, String)]()
// doesn't compile, violates case clause requirement
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
// block of code as a partial function and parentheses omission,
// i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 })
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
// curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_)
// parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft{_+_}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1>
// curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0)(_ + _)
// parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0){_ + _}
// block of code and parentheses omission
List(1, 2, 3).foldLeft {0} {_ + _}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).foldLeft(0) _ + _
// error: ';' expected but integer literal found.
List(1, 2, 3).foldLeft 0 (_ + _)
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
// block of code that just evaluates to a value of a function, and parentheses omission
// i.e. foo({ println("Hey"); x => println(x) })
foo { println("Hey"); x => println(x) }
// parentheses omission, i.e. f({x})
def f(x: Int): Int = f {x}
// error: missing arguments for method f
def f(x: Int): Int = f x
関数呼び出しでの使用法と、さまざまなことが起こる理由を説明する価値があると思います。誰かがすでに言ったように、中括弧はコードのブロックを定義します。これは式でもあるため、式が期待される場所に置くことができ、評価されます。評価されると、そのステートメントが実行され、lastのステートメント値はブロック全体の評価の結果です(Rubyのように)。
次のようなことができます。
2 + { 3 } // res: Int = 5
val x = { 4 } // res: x: Int = 4
List({1},{2},{3}) // res: List[Int] = List(1,2,3)
最後の例は、3つのパラメーターを持つ関数呼び出しであり、それぞれが最初に評価されます。
関数呼び出しでどのように機能するかを見るために、別の関数をパラメーターとして取る単純な関数を定義しましょう。
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
それを呼び出すには、Int型の1つのパラメータを取る関数を渡す必要があるため、関数リテラルを使用してfooに渡すことができます。
foo( x => println(x) )
前に述べたように、式の代わりにコードブロックを使用できるので、使用しましょう
foo({ x => println(x) })
ここで行われるのは、{}内のコードが評価され、関数値がブロック評価の値として返され、この値がfooに渡されることです。これは、前の呼び出しと意味的に同じです。
しかし、さらに何かを追加できます。
foo({ println("Hey"); x => println(x) })
コードブロックには2つのステートメントが含まれ、fooが実行される前に評価されるため、最初に「Hey」が出力され、次に関数がfooに渡され、「Entering foo」が出力され、最後に「4」が出力されます。
これは少しThisいように見えますが、Scalaを使用すると、この場合は括弧をスキップできるため、次のように記述できます。
foo { println("Hey"); x => println(x) }
または
foo { x => println(x) }
それはずっと良く見え、前のものと同等です。ここでは、まだコードのブロックが最初に評価され、評価の結果(x => println(x))が引数としてfooに渡されます。
case
を使用しているため、部分関数を定義しており、部分関数には中括弧が必要です。
parensによるコンパイルチェックの増加
Sprayの著者は、丸かっこでコンパイルチェックを強化することを推奨しています。これは、スプレーのようなDSLにとって特に重要です。括弧を使用すると、コンパイラに単一行のみを指定するように指示するため、誤って2行以上指定した場合に文句を言います。現在、これは中括弧の場合ではありません。たとえば、コードをコンパイルする場所のどこかに演算子を忘れると、予期しない結果になり、非常に見つけにくいバグになる可能性があります。以下は不自然なものです(式は純粋であり、少なくとも警告を出すため)が、
method {
1 +
2
3
}
method(
1 +
2
3
)
最初はコンパイルし、2番目はerror: ')' expected but integer literal found.
を作成者が1 + 2 + 3
に書きたかったことを示します。
既定の引数を持つマルチパラメーターメソッドについても同様であると考えることができます。括弧を使用する際に、パラメーターを区切るために誤ってコンマを忘れることは不可能です。
冗長性
冗長性についてしばしば見落とされがちな重要なメモ。 scalaスタイルガイドには、中括弧を閉じる必要があることを明確に示しているため、中括弧を使用すると、必然的に冗長なコードになります。 http://docs.scala-lang.org/style/ Declarations.html "...右中括弧は、関数の最後の行の直後の独自の行にあります。" Intellijのような多くの自動リフォーマッタは、この再フォーマットを自動的に実行します。そのため、可能な場合は丸括弧を使用するようにしてください。例えば。 List(1, 2, 3).reduceLeft{_ + _}
は次のようになります。
List(1, 2, 3).reduceLeft {
_ + _
}