純粋関数(じゅんすいかんすう、)は、コンピュータプログラミングにおいて、同一の入力に対して常に同一の出力を返し、かつ計算の過程で副作用を外部に生じさせない関数を指す。

このような関数呼び出しは、その返り値や同値の式で置き換えてもプログラムの意味が変わらないと考えられ、この性質を参照透過性という。

純粋関数は、関数型プログラミングに由来する概念であるが、実際には C# や JavaScript のように副作用を許す言語で、計算と状態変更を分離するための設計原則としても広く用いられる。

条件 一般に、ある関数が純粋であるためには、次の二条件を満たす必要がある。 同一の入力を引数にとったとき、常に同一の 出力を返す こと。 グローバル変数の変更、 引数 オブジェクト の破壊的変更、標準入出力、ファイル書き込みなどの副作用を持たないこと。

この二条件はしばしば簡潔に述べられるが、実際に何を「同一の入出力」とみなし、何を「観測可能な副作用」に含めるかは揺れうる。

コード例 純粋関数の例 Pythonで記述された次の関数は、引数だけを用いて結果を計算し、外部状態を変更しないため純粋関数である。

def hypotenuse_squared(x, y): return x * x + y * y

def fibs(count): sequence = [] a, b = 0, 1 for _ in range(count): sequence.append(a) a, b = b, a + b return sequence

fibs は引数 count に応じてフィボナッチ数列の先頭から count 個を返す。計算に使う状態は関数の内部に閉じており、外部の状態を参照・変更しないため純粋関数である。

純粋でない関数の例 次の関数は、引数以外の状態を参照しているか、副作用を持つため純粋ではない。

import time

def current_unix_time(): return int(time.time())

def append_item(items, value): items.append(value) return items

call_count = 0

def next_serial(): global call_count call_count += 1 return call_count

current_unix_time はシステム時刻に依存するため、実行のたびに結果が変わる。append_item は引数として受け取ったリストを破壊的に変更するため、副作用を持つ。next_serial はグローバル変数を更新し、その値を返すため、両方の性質を満たさない。

利点 純粋関数には、次のような実用上の利点がある。

同じ入力に対して同じ結果が得られるため、計算結果を安全にキャッシュでき、メモ化の対象にしやすい。 引数と返り値の対応を直接検証でき、外部状態の初期化や変更を前提にしなくてよいため、テスト自動化 による単体テストを書きやすい。 共有状態や副作用に起因する非決定性(実行のたびに結果が変わる可能性)を抑えやすく、並列計算や並行計算の振る舞いを推測しやすい。

入出力 標準入出力・ファイル・ネットワークのような入出力は、一般に外界との相互作用を伴うため、そのままでは純粋ではない。 純粋関数型言語では、入出力の手順や状態の遷移を値として表現し、それを関数の引数や返り値として扱うことで、言語全体の純粋性を保つ設計が採られることがある。 Haskellではこの目的のためにモナドが用いられる。

例外処理 多くのプログラミング言語の例外処理では、throw や raise によって通常の返り値とは別の制御経路で失敗を表す。

def reciprocal_or_raise(value): if value == 0: raise ZeroDivisionError("division by zero") return 1 / value

この関数には副作用がなく、振る舞い自体は決定的である。 しかし例外の伝搬は通常の返り値とは異なる経路であり、単純に返り値に置き換えて表現することはできない。

関数型プログラミングでは、失敗も返り値で表すことで参照透過性を保つ設計がしばしば採られる。 たとえば、 Haskell などでは値が存在しない可能性を Maybe型 で、成功値と失敗値の分岐を Either 型で表す。 同様の考え方は他の言語にも見られる。Rust の Result型 Result<T, E> は成功を Ok、失敗を Err で表し、Go では慣習的に複数の戻り値の組 (value, err) によってエラーを明示する。 これらはいずれも、失敗を例外として途中で処理を打ち切るのではなく、返り値の一部として表す設計である。

脚注

出典

関連項目 関数型プログラミング 参照透過性 副作用 (プログラム) メモ化 単体テスト