コンテンツにスキップ

プログラミング/遅延評価

出典: フリー教科書『ウィキブックス(Wikibooks)』

プログラミングにおいて、評価の方法にはいくつかの種類があります。その中でも「遅延評価」という手法があります。遅延評価は、プログラムの評価を必要な時まで遅らせる手法です。これにより、必要のない評価を行わなくて済み、プログラムの実行効率を向上させることができます。 本章では、遅延評価について詳しく解説し、その使い方やメリット、デメリットなどを紹介します。

遅延評価とは

[編集]

遅延評価とは、評価の結果が必要になるまで、評価を行わない手法です。例えば、以下のようなコードがあったとします。

if (foo() && bar()) {
  // do something
}
foo()とbar()が両方ともtrueを返す場合に、if文の中身が実行されます。しかし、foo()がfalseを返す場合には、bar()を評価する必要はありません。このように、遅延評価を使うことで、必要のない評価を行わず、プログラムの実行効率を向上させることができます。

遅延評価の使い方

[編集]

遅延評価を使うには、以下のような手法があります。

短絡評価演算子を使用する

[編集]

短絡評価演算子(ショートサーキット演算子)とは、「&&」や「||」のような論理演算子のことです。この演算子を使うことで、左辺がfalseの場合には右辺の評価を行わず、処理をスキップすることができます。

例えば、以下のコードを考えてみましょう。

if (x > 0 && y / x > 2) {
  // do something
}
xが0以下の場合には、y / x > 2の評価は行われません。

関数型プログラミング

[編集]

遅延評価は、関数型プログラミングにおいて非常に重要な概念であり、HaskellやScalaなどの関数型プログラミング言語で広く使用されています。遅延評価を理解することで、より効率的なコードを書くことができるようになります。

Haskell

[編集]

以下に、遅延評価を使ったHaskellのコード例を示します。

take 5 [1..]
1から始まる無限のリストを作成し、そのリストから最初の5つの要素を取り出します。しかし、このコードはリストの最初の5つの要素だけを評価し、それ以外の要素は必要になるまで評価しません。つまり、リスト全体を評価する必要がなく、プログラムの実行効率を向上させることができます。
また、Haskellでは遅延評価を利用して、無限のリストを作成することができます。

以下は、無限のフィボナッチ数列を生成するHaskellのコード例です。

fib = 0 : 1 : zipWith (+) fib (tail fib)
フィボナッチ数列を生成するために、最初の2つの要素を0と1に設定し、zipWith関数を使用してリストを生成しています。
zipWith関数は、2つのリストを引数に取り、それらの要素ごとに関数を適用した結果を含む新しいリストを生成します。
このコードは、無限のリストを生成するために再帰的な定義を使用しているため、遅延評価を利用する必要があります。
つまり、リストの各要素は必要になるまで評価されないため、無限のリストを生成しても、プログラムがクラッシュすることはありません。

以上のように、Haskellでは遅延評価が広く使用されています。遅延評価を利用することで、プログラムの実行効率を向上させることができるため、Haskellを使用する際には、遅延評価の概念を理解することが非常に重要です。

Scala

[編集]

Scalaにおける遅延評価には2つの主要な形式があります。

  1. 遅延評価値(lazy values):遅延評価値は、その値が必要になるまで計算を実行しない値です。この機能を使うと、重い計算を必要とする場合でも、必要になるまで計算を遅延させることができます。

例えば、以下のように遅延評価値を使用できます。

lazy val x = {
  println("Calculating x")
  10
}

val y = x + 5  // "Calculating x" はここで出力される
  1. 遅延評価パラメータ(by-name parameters):遅延評価パラメータは、関数の引数として使用される値で、値を必要とする箇所で評価されます。遅延評価パラメータを使用すると、関数の呼び出し時に引数を評価する必要がなくなり、関数呼び出しのオーバーヘッドを削減できます。

例えば、以下のように遅延評価パラメータを使用できます。

def example(x: => Int): Unit = {
  println("In example")
  println(x)
}

example({
  println("Calculating x")
  10
})  // "In example" と "Calculating x" が出力される
example関数はxを引数として受け取り、println(x)を呼び出しています。
引数に遅延評価パラメータを使用することで、xが評価されるまで待つことができ、example関数が呼び出された時点で"Calculating x"が出力されます。