def 関数の宣言 - Scala覚書
-
概要
関数を宣言する時に、イコール付けたり付けなかったりとか、その辺の挙動を試してみる。
@Author mwSoft
@Date 2010/11/21
@Env Scala2.8 -
関数の宣言
Scalaで関数を宣言する際には、defの後に名前を宣言して、イコールで処理を記述する
// 処理が1行の場合は、イコールの後に処理を記述できる def func1 = println( "func1" ) func1() //=> func1 // { } で囲めば2行以上の処理も書ける def func2 = { val str = "func2" println( str ) } func2() //=> func2 // 戻り値がない場合は、イコールは省略できる def func3 { val str = "func2" println( str ) } func3() //=> func3
3つ目の書き方はJavaとかCとかJavaScriptと似てるので、これが良く使われることになるのではないかと思う。けど、基本的には関数もイコールで代入するもの。
Scalaはいろんなものが省略できるので、何が基本形なのかわからなくなることがけっこうあるような気もする。
下記のように関数の中に関数を書くこともできる。
func4 def func4 { println("func4") func5 def func5 { println("func5") func6 def func6 { println("func6") } } } //= > func4 //= > func5 //= > func6
見ての通り、1行目でfunc4を呼んでるのに、その後にfunc4自体は宣言されている。defで宣言した場合は、先にコンパイラで解釈されているので宣言を後に回しても良いっぽい。
この時、func4の中から外の関数は呼べる。また、func4からfunc5も呼べる。でも、func4からfunc6を呼ぶことはできない。
-
引数付きの関数
関数が引数を必要とする場合は、関数名の後に括弧を書いて、中に変数名:型を記述する。
// 引数は括弧の中で、「変数名:型」で指定する def func1(i: Int) = println(i) func1(10) //=> 10 // 引数が複数ある場合は、カンマで繋ぐ def func2(i: Int, j: Int) { println(i * j) } func2(4, 5) //=> 20
Scalaは静的型付け言語なので、引数の型は必ず指定する。いろんな型が来る場合は、最悪Anyで渡してキャスト。
// 引数が複数ある場合は、カンマで繋ぐ def func3(i: Any, j: Int) { println(i.asInstanceOf[Int] * j) } func3(4, 5) //=> 20
Anyはすべてのクラスが継承している共通のクラス。共通の型としては他にも数値型が継承しているAnyValとか、すべての参照型が継承しているAnyRefとかがいる。
-
戻り値のある関数
戻り値は関数の宣言の後に、コロンを挟んで型を書く。尚、戻り値がある場合はイコールは省略できない。
// Intを返す def func1:Int = return 0 // Stringを返す def func2:String = { return "hoge" } // 引数がある場合は、こんな感じ def func3(i, Int, j: Int):Int { return i * j } // 戻り値が必要な場合は、イコールを省略できない def func4:Int { // <= イコールがないからエラーになる return 10 }
戻り値はreturnで返す。returnがない場合はRubyのように最後に評価された値が返る。
// returnを書かない場合は、最後に評価された値が返る def func5(i: Int, j: Int):Int = { i * j } println( func5( 4, 5 ) ) //=> 20 // ifとelseで評価するようにしても、ちゃんと拾ってくれる def func6(i: Int, j: Int):Int = { if( i > j ) i else j } println( func6( 3, 5 ) ) //=> 5 // elseの方をStringの評価にするとコンパイルエラーになる def func7(i: Int, j: Int):Int = { if( i > j ) i else "" + j //<= 最後にStringが評価されるのでエラー }
こういう処理を見ていると、コンパイラって頭いいなぁと思う。
-
defではなく、valやvarによる関数の宣言
関数はvalやvarでも宣言することができる。この場合は、
// 引数付きの関数をvalに入れる val func1 = (i: Int, j: Int) => { return i * j } println(func1(3, 4)) //=> 12 // 引数なしの関数をvarに入れる val func2 = () => { return 10 } println(func2) //=> 10
この時の戻り値の型は勝手に評価されているっぽい。
defの場合は先に評価されるので、宣言の前に呼び出しを実行することもできたけど、valやvarの場合はもちろん宣言後にしか呼び出せない
println(func3) //<= まだ宣言されていないため、エラーになる var func3 = {i: Int} => { return i * 3 }