C#7が準備するもの(パート2.パターンマッチング)

C#7の技術革新に関する一連の記事を続けて、おそらく主な技術革新であるパターンマッチングとレコードタイプ(「登録されたタイプ」のおおよその翻訳)に焦点を当てます。 これらの機能は相互に補完するため、それらについて一緒に話し合うことをお勧めします。



Recordタイプから始めましょう。 彼はF#から来ます。 本質的に、これはクラスの簡単な定義であり、そのプロパティを変更することは不可能です。 すべてのフィールドには読み取り専用パラメーターがあり、パラメーターはコンストラクターで設定されます。 これを記述するのは非常に長く退屈なので、すぐにサンプルコードから始めましょう。サンプルを見てみましょう。 レコードタイプ定義の例を次に示します。



public class Cartesian(double x: X, double y: Y);
      
      







これは、ポイントのデカルト座標を格納するクラスの定義です。 次のクラスでブロードキャストする必要があります。



 public class Cartesian { private readonly double $X; private readonly double $Y; public Cartesian(double x, double y) { this.$X = x; this.$Y = y; } public double X { get { return this.$X; } } public double Y { get { return this.$Y; } } public static bool operator is(Cartesian c, out double x, out double y) { x = cX; y = cY; return true; } override public bool Equals(object obj) { if (obj.GetType() != typeof(Cartesian)) return false; var $o = obj as Cartesian; return object.Equals(X, $oX) && object.Equals(Y, $oY); } override public int GetHashCode() { int $v = 1203787; $v = ($v * 28341) + X?.GetHashCode().GetValueOrDefault(); $v = ($v * 28341) + Y?.GetHashCode().GetValueOrDefault(); } override public string ToString() { return new System.Text.StringBuilder() .Append(“Cartesian(X: “) .Append(X) .Append(“, Y: ”) .Append(Y) .Append(“)”) .ToString(); } }
      
      





クラスのプロパティを分析しましょう。 彼の定義では、2つのdoubleパラメーターを指定しました。 これらのパラメーターは、クラス内の2つの読み取り専用プロパティと2つの読み取り専用フィールドに変換されます。 次に、クラス定義で指定されたパラメーターを使用してコンストラクターが作成されます。 また、 EqualsGetHashCodeToStringメソッドが作成されます。



最も興味深いのは、オーバーロードされたis演算子です。 ここで彼はすでにパターンマッチングに関連しています。 is演算子は、キャスト可能性の通常のチェックに加えて、追加の比較をサポートするようになりました。 このオーバーロードされた演算子への追加の呼び出しもクラスで可能です。 オペレーターがどのように過負荷になり、どのアクションを実行できるかから始めましょう。 演算子の最初のパラメーターは、渡されるクラスオブジェクトであり、この演算子のクラスである必要はありません。 次に、比較する必要がある、またはisステートメントの実行時に取得する必要がある返されたパラメーターを取得します。 レコードタイプを介してクラスを作成すると、渡されたレコードタイプクラスと定義で指定されたこのクラスの戻り値を使用してisステートメントが作成されます。 is演算子を使用してデカルト座標を極座標に変換する方法の例を次に示します。



 public static class Polar { public static bool operator is(Cartesian c, out double R, out double Theta) { R = Math.Sqrt(cX*cX + cY*cY); Theta = Math.Atan2(cY, cX); return cX != 0 || cY != 0; } }
      
      





Cartesianクラスのオブジェクトを演算子に渡すと、このクラスのデータをPolarクラスのデータに変換し、変換されたデータを返します。



パターンマッチング(またはパターンマッチング。この名前はあまり好きではありませんが、英語の定義はより正確に見えます)、それは何ですか? 彼は、PythonやF#などの言語から来ました。 本質的に、これは定数と同じ型の値を比較できるだけでなく、型キャストと必要な構造への変換を使用できる拡張スイッチです。 このすべてにおいて、新しいオーバーロードされた演算子が役立ちます。 古い型チェック演算子の新機能から始めましょう。 これの代わりに:



 var v = expr as Type; if (v != null) { //  v }
      
      





次のように書くことができます:



 if (expr is Type v) { //  v }
      
      





もちろん、これにより型キャストコードが削減されます。 しかし、パターンマッチングに戻り、彼が私たちに準備している可能性を見つけてください。 特定のデカルト座標を極座標にし、半径を取得するためのチェックを作成します。



 var c = Cartesian(3, 4); if (c is Polar(var R, *)) Console.WriteLine(R);
      
      





ここで何が起こっているのか、見てみましょう。 変数cが取得され、変数によって型が取得され、演算子検索されます。最初のパラメーターはこの型です。 次に、この演算子が呼び出され、trueが返された場合、条件が満たされたと見なされます。 次に、条件ブロックでローカル変数Rを取得します。 ここでは、角度は重要ではないため、2番目のパラメーターに*を渡しました。これは、2番目のパラメーターを無視することを意味します。 この演算子の別の使用が可能です:



 if (c is Polar(5, *)) Console.WriteLine("  5");
      
      





ここでは、半径の戻り値に追加の条件を課し、半径が5の場合にのみ条件が満たされます。



new is演算子の主な用途 、もちろんswitchステートメントです。 パターンマッチングを使用して代数式を解く例を次に示します。 レコードタイプを使用して、必要なクラスを定義します。



 abstract class Expr; class X() : Expr; class Const(double Value) : Expr; class Add(Expr Left, Expr Right) : Expr; class Mult(Expr Left, Expr Right) : Expr; class Neg(Expr Value) : Expr;
      
      





まず、微分メソッドを作成します。



 Expr Deriv(Expr e) { switch (e) { case X(): return Const(1); case Const(*): return Const(0); case Add(var Left, var Right): return Add(Deriv(Left), Deriv(Right)); case Mult(var Left, var Right): return Add(Mult(Deriv(Left), Right), Mult(Left, Deriv(Right))); case Neg(var Value): return Neg(Deriv(Value)); } }
      
      





または式の簡素化:



 Expr Simplify(Expr e) { switch (e) { case Mult(Const(0), *): return Const(0); case Mult(*, Const(0)): return Const(0); case Mult(Const(1), var x): return Simplify(x); case Mult(var x, Const(1)): return Simplify(x); case Mult(Const(var l), Const(var r)): return Const(l*r); case Add(Const(0), var x): return Simplify(x); case Add(var x, Const(0)): return Simplify(x); case Add(Const(var l), Const(var r)): return Const(l+r); case Neg(Const(var k)): return Const(-k); default: return e; } }
      
      





この機能の説明では、主に数学的計算に関連する例を紹介しました。 コメントにあなたの例を見ることができてとてもうれしいです。この機能は数学的な計算ではなく本当に便利です。



ここでソースを読むことができます



All Articles