メソッドと関数について解説

Scala 3 (Dotty 0.26.0-RC1) 2.13.3 2.12.12
最終更新:2020年5月25日

[AD] scalapediaでは記事作成ボランティアを募集しています

この記事ではメソッドと関数について説明します。

メソッドを定義する

メソッドとは、オブジェクト指向プログラミングにおけるオブジェクト内のデータを操作するためのコードの集まり(サブルーチン)です。

ここではメソッドを定義する方法を説明します。

メソッドはclassobjecttrait内にdefキーワードを使用して定義します。
形式は以下のとおりです。

def メソッドの名前(パラメーターリスト): 戻り値の型 = 式 or ブロック

以下はメソッドの定義例です。
2つのIntのパラメーターを受け取り、第一引数から第二引数を引いたInt型の結果を返しています。

object Math { def sub(x: Int, y: Int): Int = x - y }

それでは定義したメソッドを呼び出してみます。
メソッドはレシーバーとなるオブジェクトを指定して呼び出します。
先ほどのメソッドは、Mathオブジェクトに定義したので、Mathオブジェクトに対してメソッドを呼び出します。

val result = Math.sub(3, 1) println(result)

実行結果は以下のとおり第一引数から第二引数の値を引いた値が表示されます。

2

printやprintlnもメソッドです

先ほどメソッドを呼び出すにはレシーバーを指定すると説明しました。
しかし、先ほどの例でもprintlnは、レシーバーを指定せずに呼び出しています。

では、printlnはメソッドではないのでしょうか?

実はprintlnPredefオブジェクトのメソッドです。
Predefオブジェクトのメンバーは暗黙的にimportされるため、レシーバーを指定する必要はありません。

コンパイラはすべてのソースファイルに以下のインポートを追加します。

import java.lang._ { import scala._ { import Predef._ { // your code } } }

すべてのコードからjava.lang / scalaパッケージのクラスやオブジェクト、Perdefオブジェクトのメンバーをimportを書かなくても使用することができます。

Predefオブジェクトについては以下の記事を参照してください。

メソッドの中に定義する

メソッドはメソッドの中に定義することもできます。
メソッドの中に定義したメソッドはそのメソッド内からのみ呼び出すことができます。

以下の例では引数で指定した数のフィボナッチ数列を作成するメソッド「fibonacciSequence」の中にメソッド「go」を定義しています。
「fibonacciSequence」の最終行で「go」を呼び出しフィボナッチ数列を返します。

def fibonacciSequence(n: Int): List[Int] = { def go(x1: Int, x2: Int, n: Int): List[Int] = { if (n == 1) { List(x1, x2) } else { x1 :: go(x2, x1 + x2, n - 1) } } go(0, 1, n) }

定義したメソッド「fibonacciSequence」を呼び出してみます。

println(fibonacciSequence(10).mkString(", "))

実行結果は以下のとおり、0から55のフィボナッチ数列が表示されます。

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

関数を定義する

関数とは、値を受け取りなんらかの処理をするコードの集まり(サブルーチン)です。

ここでは関数を定義する方法を説明します。

関数は以下のように右辺のパラメータリストと=>、左辺の式またはブロックで構成されます。

(パラメーターリスト) => 式 or ブロック

「メソッドを定義する」で定義したメソッドを関数で定義すると以下のようになります。
以下の関数は、右辺の「x」「y」を左辺の式で評価した値(x - yの結果)に変換することを意味しています。

(x: Int, y: Int) => x - y

定義した関数を呼び出してみます。
以下の例では定義した関数を呼び出すために変数「sub」に定義した関数を保管しています。
関数を変数に保管するとはどういうことなのかは後ほど説明します。

val sub = (x: Int, y: Int) => x - y val result = sub(3, 1) println(result)

実行結果は以下の通り、メソッドの実行結果と同じです。

2

関数はFunctionトレイトの無名サブクラスのインスタンス

先ほどは定義した関数を変数「sub」に保管していました。
なぜこのようなことが出来るのでしょうか?

Scalaの関数はFunctionトレイトの無名サブクラスのインスタンスです。
「関数を定義する」で説明した形式で関数を定義するとFunctionトレイトの無名サブクラスのインスタンスが作成されます。

先ほどの例では「sub」に保管されていたのは、Function2トレイトの無名サブクラスのインスタンスです。

先ほどの例で定義した関数をFunction2トレイトで定義すると以下のようになります。
Function2[Int, Int, Int]は、2つのInt型の引数を持ちInt型の値を返す関数ことを意味しています。
作成したインスタンスに引数を指定するとapplyメソッドが呼び出されます。

val sub: Function2[Int, Int, Int] = new Function2[Int, Int, Int] { def apply(x: Int, y: Int): Int = x - y }

定義した関数を呼び出してみます。

val result = sub(3, 1) println(result)

実行結果は以下の通り「関数を定義する」の例と同じ結果になります。

2

Functionトレイトは引数の数に応じてFunction0からFunction22が定義されています。

関数は引数や戻り値にできる

関数は他の関数やメソッドの引数に指定したり、戻り値にすることができます。
このような関数を第一級関数(First class function)と呼びます。

関数を引数に指定する

関数を引数に指定する方法を説明します。
以下の例では、1から5の数値を要素に持つListmapメソッドにIntを受け取りStringを返す関数を指定しています。

val list = List(1, 2, 3, 4, 5) val result = list.map(x => x match { case 1 => "One" case 2 => "Two" case 3 => "Three" case _ => "Over Four" }) println(result.mkString(", "))

実行結果は以下のとおりです。

One, Two, Three, Over Four, Over Four

Scalaのコレクションクラスにはこのように関数を引数に指定するメソッドが多く定義されています。
このように関数を引数に指定する関数のことを「高階関数」と呼びます。

関数を戻り値にする

次に関数を戻り値にする方法を説明します。

以下は、関数を戻り値にするメソッドの例です。
「init」は引数で初期値を受け取り、呼び出されるたびに1加算する関数を返しています。

def init(i: Int): () => Int = { var count = i () => { count = count + 1 count } }

実行してみます。

val increase = init(10) println(increase()) println(increase()) println(increase())

以下のように「init」の引数で指定した値を呼び出されるたびに1加算されていることがわかります。

11 12 13

メソッドは第一級関数ではない

関数は第一級関数であることを説明しました。
一方、メソッドは第一級関数ではありません。

メソッドは第一級関数ではありませんが、引数や戻り値に指定することができます。
メソッドを引数や戻り値に指定した場合は、コンパイラが自動的に関数に変換します。
そのため、メソッドも引数や戻り値に指定することができます。

では、メソッドを引数と戻り値に指定する例を見てみます。

メソッドを引数に指定する

以下の例では、「関数を引数に指定する」の例のようにListmapメソッドの引数にIntStringに変換する以下のメソッドを指定してみます。

def int2string(x: Int): String = x match { case 1 => "One" case 2 => "Two" case 3 => "Three" case _ => "Over Four" }
val list = List(1, 2, 3, 4, 5) val result = list.map(int2string) println(result.mkString(", "))

実行結果は以下の通り、「関数を引数に指定する」の例と同じ結果になりました。

One, Two, Three, Over Four, Over Four

メソッドを戻り値にする

以下の例では、「関数を戻り値にする」の例で使用した「increase」をメソッドで定義しています。

def init(i: Int): () => Int = { var count = i def increase(): Int = { count = count + 1 count } increase } val increase: () => Int = init(10) println(increase()) println(increase()) println(increase())

実行結果は以下の通り、「関数を戻り値にする」の例と同じになりました。

11 12 13

メソッドと関数の違い

引数を受け取って値を返すという点では、メソッドと関数に違いはありません。
メソッドと関数で大きく違う点は第一級関数であるかどうかです。

メソッドも第一級関数に変換できるので混乱しやすいですが、上記の違いを覚えておくと良いでしょう。

サイト内検索