Scalaの関数とifで注意すべきこと
迷路の続きでクラスタ分けしたものを結合して道にするものを作っていました。非公開の方向で。割とまともにかけてるし今回はオブジェクティブにやった。
Scalaのifは値を返す
ところでScalaはすべての式が評価されて(戻り値のようなもの)を持つから
val x = 5 val hoge = if(x < 0) "foo" else "bar"
みたいにif文も値を持ち、
def hoge(x:Int) = { if(x < 0) "foo" else "bar"}
みたいなこともできる。returnが省略できるというのもこういうところから由来するのだろう。だがここでハマッたのが以下の様なコード
def isHoge(str:String) = { if(str == "hoge") true false }
一見すると普通のコード。きっとScalaがわからなくてcをやり出しましたみたいな大学1年にも理解できるだろう。だがこいつ、 <<<falseしか返さない>>>
適当に中間変数を交えてかけばすぐわかるが
def isHoge(str:String) = { val result = if(str == "hoge") true false }
まあresultにtrueが格納されるだけなので当然といえば当然
def isHoge(str:String) = { if(str == "hoge") true else false }
正しくはこう。例えが良くなかったけどこいつ
def isHoge(str:String) = str == "hoge"
じゃねってツッコミはなしで
評価されるのは最後の式
今のことを踏まえると途中にprintlnを入れたりしたらどうなるのかという話になる
def isHoge(str:String) = { println(str) str == "hoge" }
最後の式の評価結果が関数の評価結果として返ることになる。ちなみに
def isHoge(str:String) = { str == "hoge" println(str) } isHoge: (str: String)Unit
となりUnit(戻り値なし)と型推論され、明示的にBooleanとすると
scala> def isHoge(str:String) :Boolean = { | str == "hoge" | println(str) | } <console>:9: error: type mismatch; found : Unit required: Boolean println(str)
エラーとなる。
本題
ここまでくればわかるがifをネストさせたりした場合にこういう挙動を得ることになる
def isMatch(n:Int) : String = { if(n > 0) { if(n % 2 == 0) { "n is a positive and multiple of 2" } "n is positive number" } "n is negative number" } println(isMatch(-5)) println(isMatch(5)) println(isMatch(200))
scalaがわからなくてもだいたい出力結果は予想できると思う。しかし結果は
n is negative number n is negative number n is negative number
なのである。正しくするなら"n is ~..."節が正しく返されるように書くべきであり
def isMatch(n:Int) : String = { if(n > 0) { if(n % 2 == 0) { "n is a positive and multiple of 2" } else "n is positive number" } else "n is negative number" } println(isMatch(-5)) println(isMatch(5)) println(isMatch(200))
結果は
n is negative number n is positive number n is a positive and multiple of 2
これのせいでものすごく時間を持って行かれた気がする。どうにもうまく判定ができないわけだ。