takafumi blog

日々の勉強メモ

【Scala】for式の括弧と、セミコロン推論

環境   Scala 2.11.6 CentOS7.0

Scalaスケーラブルプログラミング」(コップ本)メモ

読了:第07章まで

■ for式の括弧と、セミコロン推論

P.134 「もしそうしたいなら、ジェネレータとフィルターは、括弧(...)ではなく中括弧{...}で囲んでもよい。中括弧で囲むと確固のときに必要なセミコロンの一部を省略できるというメリットがある」

def grep(pattern: String) =
  for (
    file <- fileHere
    if file.getName.endsWith(".scala");
    line <- fileLines(file);
    trimmed = line.trim
    if line.trim.matches(pattern)
  ) println(file + ": " + trimmed)

これを

def grep(pattern: String) =
  for {
    file <- fileHere
    if file.getName.endsWith(".scala")
    line <- fileLines(file)
    trimmed = line.trim
    if line.trim.matches(pattern)
  } println(file + ": " + trimmed)

こう書ける。


これ自体はそうなのかと思うだけだが、そもそも括弧のときに

for (
  line <- fileLines(file);
  trimmed = line.trim
  if line.trim.matches(pattern)
)

のジェネレーターにセミコロンが必要な理由が最初よくわからなかった。

という事でちょっと「4.2 セミコロン推論」に戻って考えてみる。

ここに書いてある事によると以下の場合、行末はセミコロンを省略しても、セミコロンとして扱われる。

  1. 行末が、文の末尾として文法的に認められる単語になっている。
    ピリオドや中置演算子などは認められない。
  2. 次行が文の先頭として、文法的に認められる単語である。
  3. 括弧(...)、角括弧<...>の中にいる状態で文の末尾になっていない。
    もともとこうした括弧の中に複数の文を入れる事はできない。


まず以下のように、

for (
  file <- fileHere
  if file.getName.endsWith(".scala");
  line <- fileLines(file)
  if line.trim.matches(pattern)
)

ジェネレータを複数書いたときに、なぜ一つ目のフィルタif file.getName.endsWith(".scala")の後にセミコロンがあるのか考える。
これはフィルタの行末が、文の末尾として扱えるので、セミコロンが推論されず付与されない。

そのため、明示的にセミコロンが必要になる。

つまり

for (
  file <- fileHere
  if file.getName.endsWith(".scala");
  line <- fileLines(file);
  trimmed = line.trim
  if line.trim.matches(pattern)
)

line <- fileLines(file)のところで括弧中、文の末尾として扱えるため、セミコロンを省略したときに推論されずセミコロンは付与されない

なので明示的にセミコロンをつける必要がある。


しかし中括弧{...}の場合は、3.が適用されないので、この例は全行1.2.を満たしセミコロンが付与される事になる。


じゃあ、何故ジェネレータとフィルタの間はセミコロン要らないのか(推論されて自動で付与されるのか)?
と言われると、文法的に保障されているから・・・なんだと思うが、よくわからない。

まだまだ勉強不足です。


takafumi-s.hatenablog.com