LL脳がscalaの勉強を始めたよ その11


さてScalaコップ本のなかなか進まない第5章の続きですよー

iPhone4予約で3時間も並んでしまったので5章の残りで力尽きました…orz

...というわけで今日は7節のオブジェクトの等価性についてから演算順序とかをやりますー

オブジェクトの等価性

オブジェクトの等価性という単語なんだけども、等しいか等しくないか?ドッチ?っていう内容。まあいつも使うやつだと言ってしまえばそれまでです。

  • 等しい: ==
  • 等しくない: !=

ここで”オブジェクトの”という単語がある通り、リテラルだろうがリストだろうがセットだろうが、全てのオブジェクトで比較できマス

例えばこんな感じですなー

// リテラルの等価比較
scala> 1 == 1
res0: Boolean = true
// リテラルの等価じゃない比較
scala> 1 != 1
res1: Boolean = false
// リストの等価比較
scala> List(1,2,3) == List(1,2,3)
res2: Boolean = true
// セットの等価じゃない比較
scala> Set(2,4,8) != Set(2,4,8)
res4: Boolean = false

ついでに異なる型のオブジェクトでも可能みたいね

// IntとDoubleの比較
scala> 1 == 1.0
res11: Boolean = true
// ListとDoubleの比較
scala> List(1,2,3) == "hello"
res12: Boolean = false

ここでちょっと気になったコレ

// これだと怒られるのに
scala> 1 == "hello"
<console>:5: warning: comparing values of types Int and java.lang.String using `==' will always yield false
       1 == "hello"
         ^
res17: Boolean = false
// コレだと怒られない不思議
scala> "hello" == 1
res18: Boolean = false

//// 実際の呼び出し式に書き直してみると
// 怒られるパターン
scala> (1).==("hello")
<console>:5: warning: comparing values of types Int and java.lang.String using `==' will always yield false
       (1).==("hello")
           ^
res19: Boolean = false
// 怒らないパターン
scala> ("hello").==(1)
res20: Boolean = false

どうもInt(多分数値型)のメソッドとして==を呼び出すとダメみたい。メッセージを見るとyieldが失敗するだろうから警告しときますよ…てな感じかな?

yieldってJavascriptで観たことがあるなぁ…あとjavaだとスレッド処理が云々とかいうやつだったような気がするし…一時的に処理を停止するものって解釈でいいのかしら?

等価性の比較については内部的にいくつかの段階で処理をしているみたいなので、内部的な処理の段階で数値型だと上手くいかないのかも…うん、あとで調べよう。

ちなみに等価性比較は次の順序で行われるっぽい

  1. nullかどうかの比較
  2. nullでない場合はequalsメソッドの呼び出し


…と、ここで気になったので確かめてみよう

// equalsメソッドで比較
scala> 1 equals "hoge"
res25: Boolean = false
// 逆方向に比較
scala> "hoge" equals 1
res26: Boolean = false

あれー?、なんかうまくいってますけども…null比較からequalsへの受け渡しがうまくいってないのかしら?あとはソース見ないとワカラナイんだけど自分、Javaがわからないので(高倉健的に)…教えてエロい人(`・ω・´)


あ、ちなみに等価性の判定は両辺の他の演算処理後(次節の演算子の優先順位を参照)に行われるみたいなので、こんな比較もできたりもするのですな

scala> "h" + "o" + "g" + "e" == "hoge"
res28: Boolean = true

比較の体系はJavaと若干値がうみたなんだけども後でやるみたいなので後回しー
まあ、Javaを意識しなければ悩まずに使えるんだけどね

演算子の優先順序と結合性

演算子をどの順序で計算していけばいいのかしら?っていう内容。まあ、よくあるやつですね。

まずは優先順序から

Scalaでの演算優先順序はメソッドが演算子として振舞う都合上、演算子の先頭文字によって優先順位が決められるみたいです。

例えば***という演算子hogeという演算子があれば、先頭文字*とhの比較で***のほうが先に演算されるという感じです。先頭文字ってとこだけ忘れないようにすればいいですかねー

じゃあ、演算子の優先順位の高い先頭文字を順に並べますー

ひとつだけ例外的なのは代入演算子(= や += や *= なんか)の優先度が一番低いというところ。先頭文字に関わらず、これだけは例外的に作用しますよーということです。

まあ、代入演算子が先に実行されちゃうと式の実行が悲惨な事になりますからねー

とりあえず演算順序の例をあげていきますー

// 左シフトよりも+の方が優先されますよー
scala> 2 << 2 + 2
res32: Int = 32

// 代入演算子*=は頭が*なんだけど+の法が優先される例
scala> var x = 3
x: Int = 3

scala> x *= 3 + 2

scala> x
res44: Int = 15
そんで結合性

演算子の優先順序が同じ場合の処理順序を決定するのが結合性らしい

Scalaでは結合性は基本的に左から右なんだだけども、演算子の最後の文字が:だった場合のみ逆転するみたい。

演算子の末尾が:のもの(例えば:::)は右側の被演算子のメソッドになるので(a ::: b は b.:::(a)になる)逆からの結合になるとのこと。

例えば次のような順序になる

// * 演算であれば
a * b * c
// こういう順番の処理
( a * b ) * c

// 順序が逆転する::: 演算であれば
a ::: b ::: c
// 右が先に処理される
a ::: ( b ::: c )

ただし、被演算子自体の評価は演算子に関わらず左から右に行われるので、a ::: b は実際には次のように評価されるみたい

// aの値が先に評価されております
{ val x = a; b ::: (x) }

ただしコップ本で書いてある通り、Scalaの演算順序に頼るのは構わないけど悩みそうなところは()を明示的に使った方が同僚との喧嘩は減るかもね(´・ω・`)

リッチラッパー

Scalaの基本形で使えるメソッドは演算子以外にもたくさんあるよーというお話で、それらをリッチ演算と呼ぶみたいね。

例えばこんなんが一部としてありますよー

// 大きい方を取得
scala> 0 max 5
res45: Int = 5
// 小さい方を取得
scala> 0 min 5
res46: Int = 0
// 絶対値を取得
scala> -2.7 abs
res47: Double = 2.7

ようは便利な計算方法がたくさんあるから、車輪の再発明をしないでまずはリッチ演算を対応するリッチラッパーAPIから探しやがれ!ということですね、わかります

…ということで基本型と対応するリッチラッパーは次のとおりです

  • Byte: scala.runtime.RichByte
  • Short: scala.runtime.RichShort
  • Int: scala.runtime.RichInt
  • Char: scala.runtime.RichChar
  • Srting: scala.runtime.RichString
  • Float: scala.runtime.RichFloat
  • Double: scala.runtime.RichDouble
  • Boolean: scala.runtime.RichBoolean

ちなみにリッチラッパーは暗黙の型変換使いまくりみたいなんだけど、暗黙の型変換については21章でー


どうでもいいけど暗黒の型変換とかtypoするととたんに厨二臭くなるな、うん

いじょー

おお、5章終わったー、次は関数オブジェクトだ…がんばろう(´・ω・`)