How to make multiplication operator (*) behave as short-circuit?
Asked Answered
V

4

17

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.

Valeta answered 4/5, 2013 at 14:44 Comment(8)
I suspect the answer here is "no, you can't do that - unless you manually write the short-circuit check at the top of your custom operators... a short-ish circuit"Intact
@MarcGravell by custom operators you mean overloading * or ShortCircuitMultiply ? I will probably stay with ShortCircuitMultiply implementation, just wanted to know if I'm missing something criticalValeta
You can't avoid calling longOperation() - for all the compiler knows that could have side-effects, and simply, * is not designed to be short-circuiting - it is intended to always evaluate both operandsIntact
Why not just use the & operator for multiplication?Quagga
@poke: Yes & would be normal multiplication and && would be short-circuiting.Quagga
@poke: what dont you get? "you can't overload && operator directly, but you can overload & and false operators" to achieve that behavior.Quagga
@Quagga Ah, that’s what you were referring to. Sorry, that wasn’t obvious.Castanon
I suppose there's no equivalent to C++'s expression templates in C#?Lob
C
12

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
Castanon answered 4/5, 2013 at 15:15 Comment(2)
Exactly what I was thinking, make an operator * (overload of *) whose right operand is a delegate. Of course it's just the ShortCircuitMultiply method he already has in another disguise, but it allows the infix syntax a * MethodToGetB or a * (() => heavyExpression).Carolacarolan
Thanks, I will try to stick with MyDouble operator *(MyDouble left, Func<double> right) solution for now. Hope MyDouble won't add friction to development with doublesValeta
H
13

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.

Hadron answered 4/5, 2013 at 15:7 Comment(3)
Thanks Eric, I appreciate your answer. I will stick for now with poke's alternative. By the way, now I'm already learning F# and plan to implement the same functionality there (some machine learning stuff). It would be very interesting to compare source code of both.Valeta
I could be wrong, but I think "lambda lifting" means something different than what you used it for.Lob
@Pubby: You're right, I was not using that term quite correctly. Thanks!Hadron
C
12

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
Castanon answered 4/5, 2013 at 15:15 Comment(2)
Exactly what I was thinking, make an operator * (overload of *) whose right operand is a delegate. Of course it's just the ShortCircuitMultiply method he already has in another disguise, but it allows the infix syntax a * MethodToGetB or a * (() => heavyExpression).Carolacarolan
Thanks, I will try to stick with MyDouble operator *(MyDouble left, Func<double> right) solution for now. Hope MyDouble won't add friction to development with doublesValeta
L
4

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();
        }
    }
}
Lanfranc answered 4/5, 2013 at 14:55 Comment(2)
Good note, I've forgot to bring extension methods into place. It's definitely better that simple static method ShortCircuitMultiply, but not quite z * numberValeta
Yep, but I don't think it's possible to reach that ideal! :)Lanfranc
W
1

The closest I can see is something like this:

  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
  }
Whine answered 4/5, 2013 at 15:19 Comment(5)
Dividing by zero is not defined.Castanon
Yet accountants regularly use expressions like a / (a + b) that are to display as 0, and propagate as 0, on their worksheets. The use of debits and credits allows one to know that a and b are both positive, and so the denominator == 0 implies that the numerator also == 0. In this circumstance, it is correct to define division by 0 as being equal to 0.Whine
If a and b are both positive (> 0), then a + b cannot be zero. And if you want to short-circuit to zero because you know that the dividend (a) is zero, then just check that one instead of suddenly definining an undefined operation.Castanon
Also, you are evalutating the function twice in each of the operators. That seems to be a terrible idea when LongOperation is indeed a long operation; or if it might have some additional side effects.Castanon
@poke: Thank you; see update. Debits and credits are positive or zero (except in specific situations of some systems when they can be negative only for very specific transactions, that exactly cancel another already existing transaction).Whine

© 2022 - 2024 — McMap. All rights reserved.