How to make multiplication operator (*) behave as short
I have lots of computations, specially multiplication, where first part is sometimes zero and I don't want to evaluate second operand in that case. There are at least two short-circuit operators in C#: &&
and ||
which evaluate second operand only if necessary. I want to achieve similar behavior with multiplication operator.
In .net you can't overload &&
operator directly, but you can overload &
and false
operators, so you can use your extension points to change behavior of short-circuit operator. You can find more details at this article C# operator overloading: the '&&' operator
Is there any means to achieve this or similar behavior for multiplication operator?
This is a pure syntax question, because implementation is very simple. Next method achieves exactly what I want in terms of functionality:
public static double ShortCircuitMultiply(double val, Func<double> anotherValue)
{
var epsilon = 0.00000001;
return Math.Abs(val) < epsilon ? 0 : val * anotherValue();
}
Note: this implementation is not full: in C# if you multiply 0.0
with Double.NaN
or Double.NegativeInfinity
or Double.PositiveInfinity
, you'll get NaN
, but in terms of ShortCircuitMultiply
- only zero. Let's ignore this detail and it's really irrelevant in my domain.
So now if I call it as ShortCircuitMultiply(0.0, longOperation)
where longOperation
is Func<double>
, last term won't be evaluated and result of an operation will be effectively zero.
The problem is, as I already stated, I would have lots of ShortCircuitMultiply
calls and I want to make code more readable. I want code to be similar to 0.0 * longOperation()
if that is possible.
Another note: I've tried to build wrapper on double
and create implicit casting to double and also overload *
operator. I understand, that this is probably redundant: I wanted to acheive readability, but trying to build yet another wrapper. Anyway, next code demonstrates my intent:
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);
}
}
Now if I go with:
MyDouble zero = 0;
Console.WriteLine (zero * longOperation()); //longOperation is still Func<double>
I've receive:
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
But as you can see, longOperation
is evaluated long before overloaded operator is called, and I can't substitute one of the parameters with Func
or Expression
to make it lazy.
The problem with your MyDouble
wrapper class is that you use it by calling longOperation
directly. As *
is not short circuiting, it will be called directly.
Instead, you could just make your wrapper accept a Func<double>
as the second parameter instead of the double value itself. So it would work like the ShortCircuitMultiply
function:
public static MyDouble operator *(MyDouble left, Func<double> right)
{
return Math.Abs(left.value) < epsilon ? new MyDouble(0) : new MyDouble(left.value * right());
}
Then you would use it like this:
MyDouble x = 0;
Console.WriteLine(x * LongOperation);
And even chaining works:
MyDouble x = 5;
Console.WriteLine(x * OperationReturingZero * LongOperation);
Full example
class Program
{
static void Main()
{
MyDouble x = 0;
Console.WriteLine(x * LongOperation);
MyDouble y = 5;
Console.WriteLine(y * OperationReturningZero * LongOperation);
Console.ReadLine();
}
private static double LongOperation()
{
Console.WriteLine("LongOperation");
return 5;
}
private static double OperationReturningZero()
{
Console.WriteLine("OperationReturningZero");
return 0;
}
}
class MyDouble
{
private static double epsilon = 0.00000001;
private double value;
public MyDouble(double value)
{
this.value = value;
}
public static MyDouble operator *(MyDouble left, Func<double> right)
{
Console.WriteLine("* (MyDouble, Func<double>)");
return Math.Abs(left.value) < epsilon ? new MyDouble(0) : new MyDouble(left.value * right());
}
public static MyDouble operator *(MyDouble left, MyDouble right)
{
Console.WriteLine("* (MyDouble, MyDouble)");
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);
}
}
Output:
cast to MyDouble
* (MyDouble, Func<double>)
cast to double
0
cast to MyDouble
* (MyDouble, Func<double>)
OperationReturningZero
* (MyDouble, Func<double>)
cast to double
0
There is no way to easily do what you want. The C# language is a very "eager" language in that it always evaluates operands before it runs operators, even when as you note, you could possibly skip one by knowing the other. The only exceptions are ? :
? :
, and its equivalents, &&
, ||
and ??
. (All of which can be reduced to ? :
.)
As you correctly note, you can attain laziness through use of a lambda; a Func<T>
represents a T
that will be computed on demand. But as you also correctly note, the syntax for doing so is rather heavyweight.
Consider writing your program in Haskell if you must have lazy arithmetic. It's very lazy, and I gather it is very easy to define your own operator semantics. F# is also an option and is probably easier to learn for the C# programmer.
Well you could write an extension method for double, but I'm not sure if it's really what you're looking for.
You could then have code like this:
double z = someNumberThatMightBeZero();
double r = z.Times(number);
where number
is a method returning a double.
using System;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
double z = zero();
double r = z.Times(number);
Console.WriteLine(r);
}
static double zero()
{
return 0;
}
static double number()
{
Console.WriteLine("in number()");
return 100;
}
}
public static class DoubleExt
{
public static double Times(this double val, Func<double> anotherValue)
{
const double epsilon = 0.00000001;
return Math.Abs(val) < epsilon ? 0 : val * anotherValue();
}
}
}
链接地址: http://www.djcxy.com/p/75162.html
上一篇: “无条件运营商短路”是什么意思?
下一篇: 如何使乘法运算符(*)的行为如此短