F#のパターンマッチとC#のVisitorパターンの比較

パターンマッチグというものを使うとVisitorパターン相当のことが簡単に書けるということを聞いて、関数型プログラミングに興味を持ち始めました。というわけで最近F#をさわったりしています。
で、パターンマッチというものがどういったものかわかってきたのでVisitorパターンと比較をしてみました。まぁ、Visitorパターンのほうが大変なのはまちがいないですね。自分は好きですけど。

F#のパターンマッチング(Visual Studioチュートリアルプロジェクトにあるサンプルほぼそのまま)


type Expr =
| Num of int
| Add of Expr * Expr
| Mul of Expr * Expr
| Var of string

let rec Evaluate (env:Map<string,int>) exp =
match exp with
| Num n -> n
| Add (x,y) -> Evaluate env x + Evaluate env y
| Mul (x,y) -> Evaluate env x * Evaluate env y
| Var id -> env.[id]

let envA = Map.ofList [ "a",1 ;
"b",2 ;
"c",3 ]

let expT1 = Add(Var "a",Mul(Num 2,Var "b"))
let resT1 = Evaluate envA expT1

// 結果は5
printfn "%d" resT1

上記のパターンマッチングに対応するC#でのVisitorパターンを使った実装


[TestMethod]
public void Test()
{
var env = new Dictionary<string, int> {{"a", 1}, {"b", 2}, {"c", 3}};
var expr = new Add(new Var("a"), new Mul(new Num(2), new Var("b")));
var result = new Evaluator().Evaluate(env, expr);
// 結果は 5
Assert.AreEqual(5, result);
}

public interface IExpr
{
int Accept(IDictionary<string, int> env, IVisitor visitor);
}

public class Num : IExpr
{
public int N { get; private set; }

public Num(int n)
{
N = n;
}

public int Accept(IDictionary<string, int> env, IVisitor visitor)
{
return visitor.VisitNum(env, this);
}
}

public class Add : IExpr
{
public IExpr X { get; private set; }

public IExpr Y { get; private set; }

public Add(IExpr x, IExpr y)
{
X = x;
Y = y;
}

public int Accept(IDictionary<string, int> env, IVisitor visitor)
{
return visitor.VisitAdd(env, this);
}
}

public class Mul : IExpr
{
public IExpr X { get; private set; }

public IExpr Y { get; private set; }

public Mul(IExpr x, IExpr y)
{
X = x;
Y = y;
}

public int Accept(IDictionary<string, int> env, IVisitor visitor)
{
return visitor.VisitMul(env, this);
}
}

public class Var : IExpr
{
public string Id { get; private set; }

public Var(string id)
{
Id = id;
}

public int Accept(IDictionary<string, int> env, IVisitor visitor)
{
return visitor.VisitVal(env, this);
}
}

public interface IVisitor
{
int VisitNum(IDictionary<string, int> env, Num expr);

int VisitAdd(IDictionary<string, int> env, Add expr);

int VisitMul(IDictionary<string, int> env, Mul expr);

int VisitVal(IDictionary<string, int> env, Var expr);
}

public class Evaluator : IVisitor
{
public int Evaluate(IDictionary<string, int> env, IExpr expr)
{
return expr.Accept(env, this);
}

int IVisitor.VisitNum(IDictionary<string, int> env, Num expr)
{
return expr.N;
}

int IVisitor.VisitAdd(IDictionary<string, int> env, Add expr)
{
return Evaluate(env, expr.X) + Evaluate(env, expr.Y);
}

int IVisitor.VisitMul(IDictionary<string, int> env, Mul expr)
{
return Evaluate(env, expr.X) * Evaluate(env, expr.Y);
}

int IVisitor.VisitVal(IDictionary<string, int> env, Var expr)
{
return env[expr.Id];
}
}