関数に引数を渡す場合、括弧内()
と中括弧内の形式的な違いは何ですか{}
?
私が感じたことは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 }
=> 大丈夫です。
ベストアンサー1
これについて一度書こうとしたのですが、ルールがやや曖昧なので結局諦めました。基本的にはコツをつかむしかありません。
おそらく、中括弧と丸括弧を互換的に使用できる場所、つまりメソッド呼び出しにパラメータを渡す場合に集中するのが最善でしょう。メソッドが単一のパラメータを期待する場合に限り、中括弧を丸括弧に置き換えることができます。例:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
ただし、これらのルールをよりよく理解するには、さらに知っておくべきことがあります。
括弧によるコンパイルチェックの強化
Spray の作者は、コンパイル チェックが強化される丸括弧を推奨しています。これは、Spray のような DSL では特に重要です。括弧を使用すると、コンパイラーに 1 行だけを指定するように指示することになります。そのため、誤って 2 行以上を指定すると、コンパイラーはエラーを発します。ただし、中括弧の場合はそうではありません。たとえば、どこかで演算子を忘れると、コードはコンパイルされ、予期しない結果になり、バグを見つけるのが非常に難しくなる可能性があります。以下は不自然な例ですが (式は純粋で、少なくとも警告は発せられるため)、要点を押さえています。
method {
1 +
2
3
}
method(
1 +
2
3
)
最初は をコンパイルし、2 番目は を与えますerror: ')' expected but integer literal found
。著者は を書きたかったのです1 + 2 + 3
。
デフォルト引数を持つ複数パラメータメソッドでも同様であると主張する人もいるかもしれません。括弧を使用するときに、パラメータを区切るためのコンマを誤って忘れることはあり得ません。
冗長性
冗長性について、見落とされがちな重要な注意点。中括弧を使用すると、必然的に冗長なコードになります。Scala スタイルガイド閉じ中括弧は独自の行に記述する必要があることを明確に示しています。
… 閉じ括弧は、関数の最後の行の直後に独自の行として配置されます。
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
、中括弧が必要です。関数と部分関数リテラルの違いは何かと疑問に思うかもしれませんが、答えはコンテキストです。Scala が関数を期待する場合は、関数を取得します。部分関数を期待する場合は、部分関数を取得します。両方が期待される場合は、あいまいさに関するエラーが発生します。
表現とブロック
括弧は部分式を作成するために使用できます。中括弧はコードのブロックを作成するために使用できます (これは関数リテラルではないので、関数リテラルのように使用しないように注意してください)。コードのブロックは複数のステートメントで構成され、各ステートメントはインポート ステートメント、宣言、または式になります。次のようになります。
{
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
したがって、宣言、複数のステートメント、またはそのようなものが必要な場合は、中括弧が必要です。式はステートメントであるため、括弧は中括弧内に表示されることがあります。しかし、興味深いのは、コード ブロックも式であるため、式内の任意の場所で使用できることです。
( { 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
中括弧を配置したり、コード ブロックの中括弧内に括弧を使用したりできます。
while ({x < 10}) { (x += 1) }