1. ホーム
  2. スカラ

[解決済み】Scalaにおける中括弧と括弧の正式な違い、また、どのような場合に使用すべきなのか?

2022-03-25 15:14:41

質問

関数に渡す引数を括弧で囲むのとでは、形式的にどのような違いがあるのでしょうか。 () と中括弧で囲まれた {} ?

から受けた感触は Scalaでプログラミング の本には、Scalaはかなり柔軟性があるから、一番好きなものを使えばいいということが書いてあるのですが、コンパイルできる場合とできない場合があるんです。

例えば、(あくまで例としてですが、この特定の例だけでなく、一般的なケースについて議論している回答があればありがたいです)。

val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )

=> error: simple expression の開始が不正です。

val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

=>で結構です。

解決方法は?

一度書こうとしたのですが、ルールがやや拡散しているため、結局あきらめました。基本的には、コツをつかむしかないでしょう。

中括弧と括弧を使い分けられるのは、メソッド呼び出しにパラメータを渡すときだけです。あなたは かもしれません メソッドが単一のパラメータを想定している場合のみ、括弧を中括弧に置き換えることができます。たとえば

List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter

List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

しかし、これらのルールをよりよく理解するためには、さらに知っておくべきことがあります。

パレンによるコンパイルチェックの強化

Sprayの作者は、コンパイル時のチェックを強化するために丸括弧を推奨しています。 これはSprayのようなDSLでは特に重要です。 丸括弧を使うということは、コンパイラに一行で済ませるように指示することです。したがって、誤って二行以上与えてしまうと、コンパイラは文句を言うことになります。 中括弧の場合はそうではありません。例えば、どこかに演算子を忘れてしまった場合、コードはコンパイルされてしまい、予期しない結果や、見つけるのが非常に難しいバグが発生する可能性があります。 以下は意図的なものですが(式は純粋なので、少なくとも警告は出るでしょう)、ポイントを押さえています。

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
)

最初のものはコンパイルされ、2番目のものは error: ')' expected but integer literal found . 作者が書きたかったのは 1 + 2 + 3 .

デフォルトの引数を持つ複数パラメータのメソッドについても同様で、ペレンを使っているときに誤ってパラメータを区切るためのカンマを忘れることはあり得ません。

冗長性

冗長性に関して、見落としがちな重要な注意点があります。中括弧を使用すると、必然的に冗長なコードになります。 Scalaスタイルガイド には、中括弧は必ず一行で閉じるようにと明記されています。

... 閉鎖波括弧は最終行の直後の独立した行にあります。 関数の行になります。

IntelliJのような多くの自動再フォーマッターは、この再フォーマットを自動的に行ってくれます。 ですから、できる限り丸括弧を使うようにしましょう。

Infix記法

のように、infix記法を使用する場合 List(1,2,3) indexOf (2) と書くと、パラメータが1つだけなら括弧を省略して List(1, 2, 3) indexOf 2 . これはドットノテーションの場合ではありません。

また,1つのパラメータがマルチトークン式である場合,例えば x + 2 または a => a % 2 == 0 のように、式の境界を示すために括弧を使用しなければならない。

タプル

括弧を省略できる場合もあるので、次のようにタプルに追加の括弧が必要な場合もあります。 ((1, 2)) のように、外側の括弧を省略できる場合もあります。 (1, 2) . このため、混乱が生じることがあります。

関数/部分関数リテラルで case

Scalaには、関数と部分関数リテラルのための構文があります。それは次のようなものです。

{
    case pattern if guard => statements
    case pattern => statements
}

その他の場所で使用できるのは case 文は matchcatch のキーワードを使用します。

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が関数を期待すれば、関数が得られます。Scala が部分関数を期待すれば、部分関数が得られます。両方が期待されている場合は、曖昧さに関するエラーが発生します。

式とブロック

括弧は、部分式を作るために使用します。中括弧は、コードのブロックを作るのに使います(これは ではない 関数リテラルのような使い方をしないように注意してください。) コードのブロックは複数の文で構成され、それぞれの文はimport文、宣言、または式になります。このようになります。

{
    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) }

では、お役に立てれば幸いです。