純粋関数とは?意味・特徴・メリット・非純粋関数との違いを解説!

プログラミングを学んでいると、「純粋関数」という言葉を耳にすることがあります。

でも、こんな疑問を持つ人も多いと思います。

  • 純粋関数って何が“純粋”なの?
  • 普通の関数と何が違うの?

この記事では、初心者の方でも理解できるように、純粋関数の定義・特徴・メリット・非純粋関数との違いを、具体例を交えてわかりやすく解説します。

純粋関数とは?

純粋関数とは?

純粋関数は、次の3条件を満たす関数です。

  • 同じ入力(引数)に対して、常に同じ出力を返す
  • 外部の変数や環境に依存しない
  • 副作用が発生しない

つまり、純粋関数は「外の状態に依存せず、外の状態を変えない関数」です。ではここから、上記の各条件について詳しく説明します。

補足

  • 純粋関数の対義語は非純粋関数です。
  • 純粋関数は英語で「Pure function」と言います。
  • 非純粋関数は英語で「Impure function」と言います(「Non-pure function」と言う場合もありますが「Impure function」が一般的です)。文脈によっては「副作用を持つ関数」なので「Effectful function」や「Side-effecting function」と呼ぶこともあります。

同じ入力(引数)に対して、常に同じ出力を返す

純粋関数は、入力(引数)が同じなら、何度呼んでも結果は変わりません。以下にサンプルコードを示します。

def square(x):
    return x * x

print(square(3))  # 9
print(square(3))  # 9(毎回同じ結果)

このsquare()関数は「純粋関数」です。なぜなら、結果は引数xの値だけで決まり、外部の変数や環境に依存していないからです。

外部の変数や環境に依存しない

純粋関数は、外部の変数や環境に依存しません。

例えば、以下のadd_to_base()関数は一見シンプルですが、baseという外部の変数を使っています。そのため、baseの値が変わると、同じ引数xを渡しても出力が変わってしまいます。このように、外部の変数に依存して結果が変わる関数は「純粋関数」ではなく、「非純粋関数」になります。

base = 10

def add_to_base(x):
    return x + base

副作用が発生しない

副作用(side effect)とは、関数の外の世界(外部)に影響を与えることを指します。

たとえば次のような処理はすべて副作用です。

  • 外部の変数を変更している
  • ファイルやデータベースに書き込む
  • コンソールに出力する(print()など)
  • 画面を更新する
  • ネットワーク通信を行う

これらは「関数の外部」に影響を及ぼすため、副作用があります。そのため、これらの処理がある関数は「純粋関数」ではなく「非純粋関数」となります。一例を見てみましょう。

外部の変数を変更している

count = 0

def increment():
    global count
    count += 1
    return count

この関数は呼び出すたびにcountの値が変わります。つまり、同じ入力でも結果が変わるため、これは「純粋関数」ではありません。「非純粋関数」です。

コンソール出力やログ出力を行う

def greet(name):
    print(f"Hello, {name}!")  # 出力という副作用

print()は外部(標準出力)に文字を表示する処理なので、これも副作用を持ちます。したがって、この関数も「純粋関数」ではありません。「非純粋関数」です。

純粋関数のメリット

純粋関数のメリットを以下に示します。

メリット

  • 動作が予測しやすい
    • 同じ入力なら、常に同じ出力を返します。
    • 純粋関数は外部の状態に左右されないため、バグを特定しやすく、安心して呼び出せます。
  • テストが簡単
    • 外部の状態を気にせず、引数と戻り値だけを確認すればOKです。関数単体のテストが非常に簡単になります。
    • 例: assert square(2) == 4
  • 再利用性が高い
    • 外部依存がないため、どの環境でも同じように動作します。
    • そのため、純粋関数はモジュール化・再利用に向いています。
  • 並列実行でも安全(スレッドセーフ)
    • 純粋関数は外部の状態を変更しないので、複数スレッドから同時に呼び出しても問題ありません。

「純粋関数」と「非純粋関数」の違い

純粋関数」と「非純粋関数」はどちらも関数ですが、性質がまったく違います。

繰り返しになりますが、大きな違いは次の3つです。

  • 同じ入力(引数)に対して、常に同じ出力を返すか
  • 外部の変数や環境に依存しないか
  • 副作用(外への影響)が発生するか

つまり、純粋関数は「中で完結する関数」非純粋関数は「外と関わる関数」と覚えるとわかりやすいです。

以下に違いを表形式でまとめます。

純粋関数非純粋関数
同じ入力での出力常に同じ変わる可能性あり
外部への依存なしあり
副作用なしあり
テストのしやすさ〇(引数と戻り値だけ確認すればOK)△(外部状態を考慮する必要あり)
並列実行の安全性〇(スレッドセーフ)×(外部状態を変更する恐れあり)

本記事のまとめ

この記事では『純粋関数』について、以下の内容を説明しました。

純粋関数は、

  • 同じ入力なら常に同じ出力を返し、
  • 外部に依存せず、
  • 副作用を持たない

という3つの特徴を持つ関数です。

この性質のおかげで、純粋関数はテストしやすく、再利用しやすく、並列実行にも強いという大きなメリットがあります。

お読みいただきありがとうございました。

スポンサーリンク