[C#]コンプレックスと戦うの続き
C#で複素数を扱う挑戦はまだまだ続きます。
static void Main(string[] args){
Complex<float>.SetFunc((x, y) => x + y, (x, y) => x - y, (x, y) => x * y);
Complex<float> a = new Complex<float>(1.0f, -3.0f);
Complex<float> b = new Complex<float>(2.0f, 6.0f);
var c = a + b;
var d = a - b;
var e = a * b;
Console.WriteLine(a + " + " + b + " = " + c);
Console.WriteLine(a + " - " + b + " = " + d);
Console.WriteLine(a + " * " + b + " = " + e);
}
<実行結果>
(1+-3i) + (2+6i) = (3+3i)
(1+-3i) - (2+6i) = (-1+-9i)
(1+-3i) * (2+6i) = (20+0i)
てへっ♪ やっちゃった♪
0+1iって表記は目を瞑るとしても、1+-9iはちょっと許容しがたいです。
ついでに、20+0iみたいに虚数部が0なら虚数部省略して実数っぽく見せたいところです。
ICompareableを追加してこんな感じですかね?
public class Complex<T>
where T:IComparable{
public override string ToString(){
if (im.CompareTo(0) < 0){
return "(" + re + "" + im + "i)";
}else if(im.CompareTo(0) == 0){
return re.ToString();
}else{
return "(" + re + "+" + im + "i)";
}
}
}
しかし、このコードはコンパイルは通るのですが、実行時に以下のようなエラーを発生します。
ハンドルされていない例外: System.ArgumentException: オブジェクトは Single 型でなければなりません。
場所 System.Single.CompareTo(Object value)
場所 test.Complex`1.ToString()
エラーの意味がわかりづらいですが、どうやら im.CompareTo(0)がまずいようです。
im.CompareTo(0.0f)に書き換えると動くので、0がint型なのが問題のようですね。
ということで、キャストして再挑戦。
public class Complex<T>
where T:IComparable{
public override string ToString(){
if (im.CompareTo((T) 0) < 0){
return "(" + re + "" + im + "i)";
}else if(im.CompareTo((T) 0) == 0){
return re.ToString();
}else{
return "(" + re + "+" + im + "i)";
}
}
}
エラー CS0030: 型 'int' を型 'T' に変換できません。
なるほど、Tがintへのキャストを持っているかどうかわからないため、こういう構文も許されないらしいです。
ならば…0という値を使わずに0と比較すればいいわけです。
そこで、C#の数値型はデフォルトの時0が入ることに着目しました。
これが答えに違いない!!
public class Complex<T>
where T:IComparable{
static T zero = new T();
public override string ToString(){
if (im.CompareTo(zero) < 0){
return "(" + re + "" + im + "i)";
}else if(im.CompareTo(zero) == 0){
return re.ToString();
}else{
return "(" + re + "+" + im + "i)";
}
}
}
エラー CS0304: 変数型 'T' のインスタンスは、new() 制約を含まないため、作成できません。
new()制約とは引数のないコンストラクタをもつ制約で、これならばwhere文に追加できます。
それを追加したソースがこちら。
public class Complex<T>
where T:IComparable, new(){
<中略>
}
<実行結果>
(1-3i) + (2+6i) = (3+3i)
(1-3i) - (2+6i) = (-1-9i)
(1-3i) * (2+6i) = 20
コンパイルも通るし、思い通りの結果になりました。
仮に、この小細工なしで言語拡張で対応する場合、どんな構文があればいいのか、を考えると、
intからキャストできる制約を作る、というのは理想の回答とは微妙に違う気がします。
今回欲しかった関数は、符号を得る関数ですね。
よって、理想の回答は符号を得る関数を持つインターフェースを制約条件として追加するでしょう。
ExcelにはSIGN関数がありますが、普通は理由がない限り作らない関数のため、まさにニッチすぎて誰得仕様。
なんとももやもやした結果です。