次の方法で共有


純粋関数へのリファクタリング

更新 : November 2007

純粋関数型変換で重要なのは、純粋関数を使用してコードをリファクタする方法を理解することです。

Bb669139.alert_note(ja-jp,VS.90).gifメモ :

関数型プログラミングでの命名には、一般に純粋関数を使用してプログラムをリファクタする方法が使用されます。Visual Basic および C++ では、この処理がそれぞれの言語の関数を使用して行われます。ただし C# では、関数がメソッドと呼ばれます。ここでは、純粋関数が C# のメソッドとして実装されます。

このセクションで既に説明したように、純粋関数には 2 つの実用的な特性があります。

  • 副作用がありません。この関数は、関数の外部にある変数やあらゆる型のデータを一切変更しません。

  • 一貫性があります。同じ入力データを与えられると、常に同じ出力値を返します。

関数型プログラミングに移行するには、既存のコードをリファクタして不要な副作用や外部依存関係を排除するのが 1 つの方法です。この方法で、既存のコードの純粋関数バージョンを作成できます。

このトピックでは、純粋関数の特徴とそれ以外の関数の特徴について説明します。「WordprocessingML ドキュメント内の情報の操作」チュートリアルでは、WordprocessingML ドキュメントの操作方法を説明し、純粋関数を使用してリファクタする方法を示す 2 つの例を紹介しています。

副作用と外部依存関係の排除

次の例に示す 2 つの非純粋関数と 1 つの純粋関数を参照して、その違いを確認してください。

クラス メンバを変更する非純粋関数

次のコードの HypenatedConcat 関数は、クラス内の aMember データ メンバを変更するため、純粋関数ではありません。

public class Program
{
    private static string aMember = "StringOne";

    public static void HypenatedConcat(string appendStr)
    {
        aMember += '-' + appendStr;
    }

    public static void Main()
    {
        HypenatedConcat("StringTwo");
        Console.WriteLine(aMember);
    }
}
Module Module1
    Dim aMember As String = "StringOne"

    Public Sub HypenatedConcat(ByVal appendStr As String)
        aMember = aMember & "-" & appendStr
    End Sub

    Sub Main()
        HypenatedConcat("StringTwo")
        Console.WriteLine(aMember)
    End Sub
End Module

このコードを実行すると、次の出力が生成されます。

StringOne-StringTwo

この場合、変更されるデータに public アクセスと private アクセスのどちらがあるか、またはこのデータが static (shared) メンバとインスタンス メンバのどちらであるかは関係ありません。純粋関数は、関数の外部にあるデータを一切変更しません。

引数を変更する非純粋関数

同じ関数の次のバージョンは、そのパラメータである sb の内容を変更するため、純粋関数ではありません。

public class Program
{
    public static void HypenatedConcat(StringBuilder sb, String appendStr)
    {
        sb.Append('-' + appendStr);
    }

    public static void Main()
    {
        StringBuilder sb1 = new StringBuilder("StringOne");
        HypenatedConcat(sb1, "StringTwo");
        Console.WriteLine(sb1);
    }
}
Module Module1
    Public Sub HypenatedConcat(ByVal sb As StringBuilder, ByVal appendStr As String)
        sb.Append("-" & appendStr)
    End Sub

    Sub Main()
        Dim sb1 As StringBuilder = New StringBuilder("StringOne")
        HypenatedConcat(sb1, "StringTwo")
        Console.WriteLine(sb1)
    End Sub
End Module

このバージョンのプログラムは、最初のバージョンと同じ出力を生成します。これは、HypenatedConcat 関数が Append メンバ関数を呼び出して最初のパラメータの値 (状態) を変更したためです。HypenatedConcat はパラメータを値で渡しますが、それでもこの変更は行われるので注意してください。

Bb669139.alert_caution(ja-jp,VS.90).gif重要 :

参照型の場合、パラメータを値で渡すと、渡されるオブジェクトへの参照がコピーされて渡されます。このコピーは、参照変数が新しいオブジェクトに割り当てられるまで、元の参照と同じインスタンス データに関連付けられたままとなります。パラメータを変更する場合に、必ずしも関数に参照を渡す必要はありません。

純粋関数

次のバージョンのプログラムは、HypenatedConcat 関数を純粋関数として実装する方法を示しています。

class Program
{
    public static string HyphenatedConcat(string s, string appendStr)
    {
        return (s + '-' + appendStr);
    }

    public static void Main(string[] args)
    {
        string s1 = "StringOne";
        string s2 = HyphenatedConcat(s1, "StringTwo");
        Console.WriteLine(s2);
    }
}
Module Module1
    Public Function HyphenatedConcat(ByVal s As String, ByVal appendStr As String) As String
        Return (s & "-" & appendStr)
    End Function

    Sub Main()
        Dim s1 As String = "StringOne"
        Dim s2 As String = HyphenatedConcat(s1, "StringTwo")
        Console.WriteLine(s2)
    End Sub
End Module

このバージョンも、同じ出力行 StringOne-StringTwo を生成します。この連結された値を保持するために、中間変数 s2 が使用されていることに注意してください。

ローカルには純粋ではないが (つまりローカル変数を宣言して変更する)、グローバルには純粋である関数を作成すると、非常に便利な場合があります。このような関数は、必要に応じて構成できる特性を多く備えていますが、関数型プログラミングの複雑な表現方法の一部 (単純なループによる処理を行う場合は再帰を使用する必要があるなど) が省かれています。

標準クエリ演算子

標準クエリ演算子の重要な特性は、純粋関数として実装される点です。

詳細については、「標準クエリ演算子の概要」を参照してください。

参照

概念

純粋関数型変換の概要

関数型プログラミングと命令型プログラミング