サンプルとして、2つの引数をとり、それらを掛け合わせたものを返す関数とメソッドを作ってみます。
もっともオーソドックスな関数
まず関数の宣言方法は以下です。
scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => { x * y } multiply: (Int, Int) => Int = <function2> # 実行 scala> multiply(5,10) res8: Int = 50
(Int, Int) => Int
この部分が関数の型の宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返す関数という宣言になります。
(x:Int, y:Int) => { x * y }
この部分が実際の関数の宣言です。
なお、=>{...}の部分は、通常コードブロックと呼ばれます。
xとyという変数で値を受け取って、x * yを実行します。
Scalaでは最後に評価された式が戻り値となりますので、x*yの実行結果(Intどうしの掛け算なので当然Int型)が帰ります。
なお、実はこの関数は無名関数です。
multiplyというのはあくまで変数名であり、この関数自体には名前がありません。
「(Int, Int) => Int 」という型の変数に、イコール(=)を使って、関数「 (x:Int, y:Int) => { x * y }」を代入しているということになります。
もっともオーソドックスなメソッド
次はメソッドで同じものを作ってみます。
scala> def multiply(x:Int, y:Int):Int = { x * y } multiply: (x: Int, y: Int)Int # 実行 scala> multiply(5,10) res9: Int = 50
def multiply(x:Int, y:Int):Int
この部分がメソッドの宣言です。
第1引数にInt、第2引数にIntをとり、最終的にIntを返すメソッドという宣言になります。
= { x * y }
この部分が実際のメソッド本体ですです。
xとyという変数で値を受け取って、x * yを実行します。
関数の省略記法
幾つかパターンはありますがとりあえず以下の2パターンが良く使われると思います。
# コードブロックの中括弧{}を省略 scala> val multiply:(Int, Int) => Int = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 思い切って関数の型を省略 scala> val multiply = (x:Int, y:Int) => x * y multiply: (Int, Int) => Int = <function2> # 引数なしの関数はこれ scala> val multiply:() => Int = () => 10 * 5 multiply: () => Int = <function0> # 関数の型を省略 scala> val multiply = () => 10 * 5 multiply: () => Int = <function0>
メソッドの省略記法
結構パターンがあります。この省略記法を覚えるのには苦労させられます。
まだまだパターンはありますが、とりあえずこれでメソッドの省略記法のメジャーどころはつぶせたかなと思います。
# コードブロックの中括弧{}を省略 scala> def multiply(x:Int, y:Int):Int = x * y multiply: (x: Int, y: Int)Int # 戻り値の型を省略 scala> def multiply(x:Int, y:Int) = x * y multiply: (x: Int, y: Int)Int # 引数も戻り値の型も中括弧も全て省略。クラスのゲッターなどに使える scala> def getName = "Yamada Tarou" getName: java.lang.String # これでも同じ scala> def getName() = "Yamada Tarou" getName: ()java.lang.String # 戻り値の型と中括弧を省略したパターン。クラスのセッターなどに使える scala> def setName(newName:String) = name = newName setName: (newName: String)Unit # 戻り値が無い(Unit)の場合は、コードブロックの前のイコールを書かなくても良い scala> def myPrint { println("Hello World") } myPrint: Unit # もちろん書いても良い scala> def myPrint = { println("Hello World") } myPrint: Unit # これも同じ scala> def myPrint:Unit = { println("Hello World") } myPrint: Unit # ただしこれはNG scala> def myPrint:Unit { println("Hello World") }
関数の宣言に関する注意
ちゃんと関数として宣言せずに、とりあえずコードブロックで囲めばいいんでしょ?というノリでコーディングすると意図しない動作になってしまいます。
また、関数はオブジェクトなので、別の変数に格納することもできます。
# 以下のような宣言をすると、myPrintにはUnitが入ってしまう scala> val myPrint = { println("Hello World") } Hello World myPrint: Unit = () # なので、何回myPrintを実行しても何も起こらない scala> myPrint() # 意図した関数として宣言するには以下のようにする scala> val myPrint:()=>Unit = () => { println("Hello World") } myPrint: () => Unit = <function0> scala> myPrint() Hello World scala> myPrint() Hello World
# まずは関数定義 scala> val myPrint = () => {println("Hello World")} myPrint: () => Unit = <function0> # 括弧無しで呼び出すと関数本体が返される scala> myPrint res1: () => Unit = <function0> # 別の変数に代入 scala> val myPrint2 = myPrint myPrint2: () => Unit = <function0> scala> myPrint2() Hello World
メソッドの宣言に関する注意
Scalaでは、メソッドに戻り値があっても戻り値の型は省略できますが、メソッド宣言とコードブロックの間のイコールは省略できません。
厳密に言うと省略は出来るのですが、イコールを書略してしまうと値を返してくれないメソッドになってしまいます。
これは、イコールを省略するとメソッドが自動的にUnitになってしまうためです。
# コードブロックで文字列が評価されてStringが返りそうだけど・・・ scala> def getString { "Helo World" } getString: Unit # 実行すると何も帰ってきません scala> getString
イコールを付ければ期待した動作になります。
次に、関数の場合は括弧()なしで実行すると関数自体が返されましたが、メソッドは動作が異なります。
scala> def getString = { "Helo World" } getString: java.lang.String scala> getString res24: java.lang.String = Helo World
メソッドの宣言で、引数リストの括弧()を省略した場合には呼び出し方法が制限されます。
引数なしであっても、引数リストを省略せずに空の括弧()を付けておけばどちらでも実行できます。
scala> def myPrint { println("Hello World") } myPrint: Unit # こちらはOK scala> myPrint Hello World # 括弧を付けるとエラー・・・ scala> myPrint() <console>:9: error: Unit does not take parameters myPrint() ^
scala> def myPrint() { println("Hello World") } myPrint: ()Unit scala> myPrint() Hello World scala> myPrint Hello World