読者です 読者をやめる 読者になる 読者になる

夢とガラクタの集積場

落ちこぼれ三流エンジニアである管理人の夢想=『夢』と、潰えた夢=『ガラクタ』の集積場です。

scalaで出来ることを並べてみる:関数リテラル&関数オブジェクト

関数リテラルについて色々試してた際に、
プログラミング言語Scala 日本語情報サイト:メソッドから関数オブジェクトを取得する で
メソッドから関数オブジェクトを取得する方法もわかったので併せて書いてます。

てなわけで。
Scalaでは関数リテラルという方式で関数を定義し、フィールドに設定して使うことが可能です。

サンプルコード

とまぁ、ただそれだけ書いても一体何のことかわからないでしょうから、
サンプルコードを書いてみます。

package jp.gr.kmtn.scalatutorial.grammertips

/**
 * 関数リテラル等関数の扱いについて確認するテストプログラム
 */
object FunctionVariationMain {
  /**
   * プログラムエントリポイント
   */
  def main(args: Array[String]): Unit =
    {
      // incrementFunctionのリテラル表記
      val incrementFuncLiteral = (target: Int) => target + 1
      // squareFunctionのリテラル表記
      val squareFuncLiteral = (target: Int) => target * target
      // doubleFunctionのリテラル表記
      val doubleFuncLiteral = (target: String) => target * 2
      // multiFunctionのリテラル表記
      val multiFuncLiteral = (first: Int, second: Int) => first * second

      println("incrementFuncLiteral: " + incrementFuncLiteral);
      println("incrementFuncLiteral(5): " + incrementFuncLiteral(5));

      println("squareFuncLiteral: " + squareFuncLiteral);
      println("squareFuncLiteral(5): " + squareFuncLiteral(5));

      println("doubleFuncLiteral: " + doubleFuncLiteral);
      println("doubleFuncLiteral(\"test\"): " + doubleFuncLiteral("test"));

      println("multiFuncLiteral: " + multiFuncLiteral);
      println("multiFuncLiteral(5, 6): " + multiFuncLiteral(5, 6));

      // この時点でfuncReferenceの型は「(Int) => Int」と確定
      var funcReference = incrementFunction _
      println("funcReference: " + funcReference);
      println("funcReference(8): " + funcReference(8));

      funcReference = squareFunction _
      println("funcReference: " + funcReference);
      println("funcReference(8): " + funcReference(8));

      // コンパイルエラー「type mismatch; found : String required: Int」
      // funcReference = doubleFunction _
      var funcReferenceDouble = doubleFunction _
      println("doubleFunction: " + funcReferenceDouble);
      println("doubleFunction(\"target\"): " + doubleFunction("target"));

      // コンパイルエラー「type mismatch; found : (Int, Int) => Int required: (Int) => Int」
      // funcReference = multiFunction _
      var funcReferenceSecond = multiFunction _
      println("funcReferenceSecond: " + funcReferenceSecond);
      println("funcReferenceSecond(8 * 9): " + funcReferenceSecond(8, 9));
    }

  /**
   * 指定した引数をインクリメントした値を返す
   */
  def incrementFunction(target: Int): Int =
    {
      target + 1
    }

  /**
   * 指定した引数を二乗した値を返す
   */
  def squareFunction(target: Int): Int =
    {
      target * target
    }

  /**
   *  指定した文字列を2度繰り返した値を返す
   */
  def doubleFunction(target: String): String =
    {
      target * 2
    }

  /**
   *  指定した引数をかけ合わせた値を返す
   */
  def multiFunction(first: Int, second: Int): Int =
    {
      first * second
    }
}

実行結果

上記のコードを実行した結果は下記のようになります。

incrementFuncLiteral: 
incrementFuncLiteral(5): 6
squareFuncLiteral:
squareFuncLiteral(5): 25
doubleFuncLiteral:
doubleFuncLiteral("test"): testtest
multiFuncLiteral:
multiFuncLiteral(5, 6): 30
funcReference:
funcReference(8): 9
funcReference:
funcReference(8): 64
doubleFunction:
doubleFunction("target"): targettarget
funcReferenceSecond:
funcReferenceSecond(8 * 9): 72

コード解説

下記2つは動作としては同じものです。
単に、関数リテラルで定義したか、通常の関数で定義したかの差分のみ。

// 関数リテラル定義
val incrementFuncLiteral = (target: Int) => target + 1
// 通常の関数定義
def incrementFunction(target: Int): Int =
  {
    target + 1
  }

定義した関数を下記のコードで変数にセットしています。
Scalaでは関数も変数にセットして保持可能です。
尚、その際の型は『』となっています。
には引数の型、返り値の型を指定可能になっており、
引数型、返り値型が異なる場合、var変数でも再設定は出来ないようです。
(funcReference = doubleFunction _としようとするとコンパイルエラーになる。)

// 関数リテラルを変数にセット
val incrementFuncLiteral = (target: Int) => target + 1
// 関数オブジェクトを変数にセット
var funcReference = incrementFunction _

変数にセットした関数リテラル/オブジェクトは通常の関数と同じく、下記の形式で実行可能です。

// 関数リテラルをセットした変数を実行
incrementFuncLiteral(5)
// 関数オブジェクトをセットした変数を実行
funcReference(8)

Cのように関数ポインタを変数に持てて、かつ型がコード上で確定するため、
『関数渡し』による処理の切り分けが非常に簡単にできるわけですね。