2011-12-15

メソッド呼び出しと演算子記法

Scala Advent Calendar JP 2011 の15日目です。
Scala には Java でいうような演算子はありません。例えば
1 + 1
というものも実際には、メソッドを呼び出しています。つまり以下のように書き換えられるということです。
1.+(1)
前者のメソッドが演算子のように見えるメソッド呼び出し方法は演算子記法と言います。基本的には、この演算子記法は通常のメソッド呼び出しに置き換えられますが、完全に一致するかというとそんなことはありません。例えば以下のような場合です。
1 + 2 * 3
これを REPL で実行すると (実行するまでもないかもしれませんが) 以下のようになります。
scala> 1 + 2 * 3
res0: Int = 7
直感とあうため特に疑問に思うことはありません。これを通常のメソッド呼び出しで書き直すと以下のようになります。
scala> 1.+(2).*(3)
res1: Double = 7.0
あれ? なんかおかしいですね。1.+(2) の部分がメソッド呼び出しではなく 1. + (2) => 1.0 + (2) のように演算子記法と解釈されています。ちょっと例が悪かったので仕切り直します。まずは演算子記法です。
scala> "foo" + "bar" * 3
res8: java.lang.String = foobarbarbar
次に通常のメソッド呼び出しです。
scala> "foo".+("bar").*(3)
res9: String = foobarfoobarfoobar
このように、演算子記法を使うとメソッドは単に前から順に呼び出されるわけではなく、呼び出しに優先順位が考慮されることが改めてわかります。優先順位はメソッドの 1文字目によって決まっていて以下の通りです。
  1. 他の全ての特殊文字
  2. * / %
  3. + -
  4. :
  5. = !
  6. < >
  7. &
  8. ^
  9. |
  10. 全ての英字
  11. 全ての代入演算子
1番目の "他の全ての特殊文字" には例えば "~" (チルダ) があります。
class Foo(val name: String) {
  def ~(foo: Foo): Foo = new Foo("~" + name + foo.name + "~")
  def *(foo: Foo): Foo = new Foo("*" + name + foo.name + "*")
  override def toString: String = name
}

val foo = new Foo("foo")
val bar = new Foo("bar")
val baz = new Foo("baz")

println(foo * bar ~ baz)
println(foo.*(bar).~(baz))
REPL で実行すると以下の結果を得ます。
scala> *foo~barbaz~*

scala> ~*foobar*baz~
演算子記法の場合は "~" のほうが "*" よりも優先順位が高くなっていることがわかります。 ということで、演算子記法はメソッド呼び出しではあるが、通常のメソッド呼び出しとは異なり、メソッドの 1文字目を見て優先順位が考慮されるということを押さえておくと変なところではまらずに済むことがあるかもしれません。

0 件のコメント:

コメントを投稿