正格関数と非正格関数について

みなさん、こんにちは。id:ogxwxです。

今回は、自分なりに正格関数と非正格関数の違いについて簡単に説明したいと思います。

まずいきなりですが、正格関数とは何か説明していきます。

正格関数

正格関数とは、引数に値を渡した時点で評価される関数のことです。

def f(n: Unit): Unit = {
    println("start")
    n
}

こんな感じの関数です。普通の関数定義ですね〜。

次に実際に関数を呼び出してみたいと思います。

今回は引数の評価順をわかりやすくf(println("end"))このような形で呼び出しを行いました。

実際の結果は以下のようになります。

end
start

本来であれば

start
end

と評価されると思った方もいると思いますが、正格関数は引数に値を渡した時点で評価されるため、このような結果となってしまいます。

では、どうすれば想定する評価順にできるのでしょうか。

非正格関数

ここでは非正格関数について説明したいと思います。

非正格関数とは引数で値を評価せず、関数内でその引数が呼び出された時に評価する関数のことです。文だとわかりにくいので以下に非正格関数のサンプルを作成しました。

def lazyF(n: => Unit): Unit = {
    println("start")
    n
}

ほぼ正格関数と同じ定義ですが、引数の型の定義方法が異なります。Scalaでは引数の型に=>を追加するだけで、引数の評価方法を非正格評価にすることができます。これによって非正格関数を作成することができます。

非正格関数の呼び出し方法は正格関数と同じく、引数に評価させたい値を渡すだけでOKです。実際の非正格関数呼び出し時のものです。

lazyF(println("end"))

すると想定される評価で表示されました。

start
end

この結果から、非正格関数は関数内で引数を呼び出した時に値が評価されることがわかります。正格関数だと引数に値を渡した時点で評価されているので、非正格関数は正格関数に比べて引数の評価地点を遅延させているんですね〜

もう一つの非正格関数

ここでは、別の非正格関数の定義方法について説明したいと思います。 Scalaは、関数の引数や返り値に関数を定義することができます(高階関数)。 そして先ほど引数の型定義に=>を追加することで、正格関数から非正格関数を作成することができると説明しましたが、他にも引数の型定義に() => と定義しても非正格関数を作成することもできます。引数の型を関数にするだけですね。またその引数を関数内で呼び出すには、関数呼び出しの呼び出し方法で書く必要があります。 以下に() => を使った関数を定義してみました。

def lazyG(n: () => Unit): Unit = {
    println("start")
    n()
}

また、この関数呼び出し方法は最初に紹介した関数呼び出し方法と少し異なります。 異なる点は、引数の型が関数であるため明示的に引数に関数を定義させる必要がある点です。 以下のように定義してあげる必要があります。

lazyG(() => println("end"))

結果は最初に紹介した非正格関数と同じ結果になります。

start
end

まとめ

自分なりに正格関数と非正格関数について説明してみました。ちなみに正格評価は、ほとんどのプログラミング言語での評価方法でもあり、非正格の評価方法もif...elseの評価方法でもあります。また、遅延リストには非正格評価があったりと正格・非正格の評価方法は幅広く使われている技術でもあります。なので、もし気になる方は調べてみてください。

最後ですが、今回紹介した正格関数と非正格関数の違いについてのコードをgistにまとめました。ありがとうございました〜

gist.github.com