16

私は多くの計算、特に乗算を行っています。最初の部分がゼロの場合があり、その場合は 2 番目のオペランドを評価したくありません。C# には少なくとも 2 つの短絡演算子があります: &&and||は、必要な場合にのみ 2 番目のオペランドを評価します。乗算演算子で同様の動作を実現したい。

.netでは&&、演算子を直接オーバーロードすることはできませんが&false演算子をオーバーロードできるため、拡張ポイントを使用して短絡演算子の動作を変更できます。詳細については、この記事C# operator overloading: the '&&' operatorを参照してください。

乗算演算子に対してこれまたは同様の動作を実現する手段はありますか?

実装は非常に単純であるため、これは純粋な構文の問題です。次の方法は、機能面で私が望むものを正確に実現します:

public static double ShortCircuitMultiply(double val, Func<double> anotherValue)
{
    var epsilon = 0.00000001;
    return Math.Abs(val) < epsilon ? 0 : val * anotherValue();
}

注: この実装は完全ではありません。C#では、 or で乗算0.0すると が得られますが、- に関してはゼロのみです。この詳細は無視しましょう。私のドメインではまったく関係ありません。Double.NaNDouble.NegativeInfinityDouble.PositiveInfinityNaNShortCircuitMultiply

したがって、ShortCircuitMultiply(0.0, longOperation)where longOperationisとして呼び出すとFunc<double>、最後の項は評価されず、操作の結果は事実上ゼロになります。

問題は、既に述べたように、多くのShortCircuitMultiply呼び出しがあり、コードをより読みやすくしたいということです。0.0 * longOperation()可能であれば、コードを次のようにしたいと考えています。


double別のメモ: ラッパーを構築し、暗黙の二重キャストとオーバーロード*演算子を作成しようとしました。これはおそらく冗長であることを理解しています。読みやすさを実現したかったのですが、さらに別のラッパーを構築しようとしています。とにかく、次のコードは私の意図を示しています。

class MyDouble
{
    double value;
    public MyDouble(double value)
    {
        this.value = value; 
    }

    public static MyDouble operator *(MyDouble left, MyDouble right) 
    {
        Console.WriteLine ("* operator call");
        return new MyDouble(left.value * right.value);
    }

    public static implicit operator double(MyDouble myDouble)
    {
        Console.WriteLine ("cast to double");
        return myDouble.value;
    }

    public static implicit operator MyDouble(double value)
    {
        Console.WriteLine ("cast to MyDouble");
        return new MyDouble(value);
    }
}

今私が行くなら:

MyDouble zero = 0;

Console.WriteLine (zero * longOperation()); //longOperation is still Func<double>

私は受け取りました:

cast to MyDouble            
called longOperation        <-- want to avoid this (it's printed from longOperation body)
cast to double             
cast to MyDouble
* operator call
cast to double   
0

しかし、ご覧のとおり、longOperationオーバーロードされた演算子が呼び出されるずっと前に評価され、パラメーターの 1 つを代用したり、遅延させFuncたりすることはできません。Expression

4

4 に答える 4

13

やりたいことを簡単にできる方法はありません。C# 言語は、オペレーターを実行する前に常にオペランドを評価するという点で、非常に「熱心な」言語です。唯一の例外は? :、 とそれに相当する 、&&||および??です。(これらはすべて に減らすことができます? :。)

お気づきのとおり、ラムダを使用して怠惰を実現できます。aは、オンデマンドで計算される a をFunc<T>表します。Tしかし、あなたも正しく指摘しているように、そうするための構文はかなり重いです。

遅延演算が必要な場合は、Haskell でプログラムを作成することを検討してください。非常に怠惰で、独自の演算子セマンティクスを定義するのは非常に簡単だと思います。F# もオプションであり、おそらく C# プログラマーにとって習得が容易です。

于 2013-05-04T15:07:50.380 に答える
1

私が見ることができる最も近いものは次のようなものです:

  struct LazyDouble {
    Func<double> Func;

    public LazyDouble(Func<double> func) : this() { Func = func; }

    public static implicit operator LazyDouble (double value) { 
      return new LazyDouble(()=>value); 
    }

    public static LazyDouble operator * (LazyDouble lhs, LazyDouble rhs) {
      var lhsValue = lhs.Func();
      if ( lhsValue == 0)
        return 0;
      else 
        return new LazyDouble(() => lhsValue * rhs.Func());
    }
    // other operators as necessary
  }
于 2013-05-04T15:19:07.633 に答える