Scala勉強中にfor文がわからなかったので考えました。
的外れなことを言ってるかもしれないので、そのときは突っ込みをお願いします。
Scalaのfor文
Scalaプログラミング入門の読書中にfor文が出てきました。
for {i <- 1 to 10
j <- 1 to 10}
println(i * j)
何これ気持ち悪い。
これでどうなるのかは何か他の言語をやっていればわかります。
標準出力(とは限らないけども)に対してi*jの結果を100行出力しているだけですよね。
それはわかるのですが、手続き型言語にはない気持ち悪さを感じます。
手続き型言語のfor文
PHPの場合だったら
for ($i = 1; $i <= 10; $i++) {
for ($j = 1; $j <= 10; $j++) {
echo ($i * $j) . "\n";
}
}
という感じでわかりやすいですね。
言語によりますが、変数に値を設定して処理を実行し、条件に合わなくなるまで処理を続けるだけです。
今回のような二重ループでも考え方は同じで、あくまでも一回目のループとその中での二回目のループとあるだけです。
でも、Scalaのfor文(特に二重ループのとき)はこの説明に一致してないように思えます。
たぶん、気持ち悪さの鍵はここにありそうです。
ScalaのBNF
構文で分からないときはBNFです。
ほとんどの言語でBNFは公開されているので覚えておくと構文を調べやすくて便利です。
[The Scala Language
Specification
Version 2.9
上のP89のFor Comprehensions and For Loopsにfor文のBNFがあります。
Syntax: Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr Enumerators ::= Generator {semi Enumerator} Enumerator ::= Generator | Guard | Pattern1 ‘=’ Expr Generator ::= Pattern1 ‘<-’ Expr [Guard] Guard ::= ‘if’ PostfixExpr
ここでの肝が Enumerators になります。
気持ち悪さの鍵はこれです。(たぶん)
手続き型言語のfor文では変数の準備とループの終了条件を定義しましたが、Scalaのfor文では列挙の定義をします。
そのため、変数を準備して終了条件を満たすというまで処理を実行するのではなく、列挙された値の組み合わせに対して処理を実行すると考えると腑に落ちます。
つまりは手続き型言語はフロー制御としてfor文がありますが、Scalaでは列挙された値の組み合わせの操作としてfor文があるようです。
ちなみにScalaにはbreakやcontinueがないというのもそこの違いで考えると腑に落ちます。
(この記事を見るとbreakはあるようですけど汚すぎるだろうと)
手続き型言語ではフロー制御としてbreak、continueがありますが、Scalaではそもそもフローなんてなくて、列挙された値の組み合わせに対して処理を実行するだけだから構文として用意されていないのだろうなと。
おまけ
ここまで書いて日本語リファレンス見つけたのでしっかり読みました。
上の6.19 for 内包表記と for ループに書いてある通り、Scalaのfor文はただのエイリアスですね。
(1 to 10).foreach { case i => (1 to 10).foreach { case j => println(i * j) } }
最初に記載したコードは内部的に上記のコードに置き換えられるようです。
なんかいろいろと無駄な考察をしましたが、要はコレクション操作ですよ。それだけの話ですよ。
JSerとしては最初からこっちを出しておいてくれれば何も考えなかったのにという気分です。
Scalaは構文レベルで脳みその切り替えが必要なので、なかなか難しいです。
動くアプリケーションを作るだけならそこまで苦労しなさそうですが、思想と作法が全然わかんねーのでちゃんとしたコードを書けるようになるには結構かかりそうですね。