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];
}
}