views:

351

answers:

2

I don't think it's possible to use operators as a parameters to methods in C# 3.0 but is there a way to emulate that or some syntactic sugar that makes it seem like that's what's going on?

I ask because I recently implemented the thrush combinator in C# but while translating Raganwald's Ruby example

(1..100).select(&:odd?).inject(&:+).into { |x| x * x }

Which reads "Take the numbers from 1 to 100, keep the odd ones, take the sum of those, and then answer the square of that number."

I fell short on the Symbol#to_proc stuff. That's the &: in the select(&:odd?) and the inject(&:+) above.

+5  A: 

Well, in simple terms you can just use a lambda:

public void DoSomething(Func<int, int, int> op)
{
    Console.WriteLine(op(5, 2));
}

DoSomething((x, y) => x + y);
DoSomething((x, y) => x * y);
// etc

That's not very exciting though. It would be nice to have all those delegates prebuilt for us. Of course you could do this with a static class:

public static class Operator<T>
{
     public static readonly Func<T, T, T> Plus;
     public static readonly Func<T, T, T> Minus;
     // etc

     static Operator()
     {
         // Build the delegates using expression trees, probably
     }
}

Indeed, Marc Gravell has done something very similar in MiscUtil, if you want to look. You could then call:

DoSomething(Operator<int>.Plus);

It's not exactly pretty, but it's the closest that's supported at the moment, I believe.

I'm afraid I really don't understand the Ruby stuff, so I can't comment on that...

Jon Skeet
Excellent answer, the Operator class seems to be pretty much exactly what I was looking for. Will have try it out later though.
Jonas Elfström
+1  A: 

The following is direct, literal (as much as possible) C# translation:

(Func<int>)(x => x * x)(
    Enumerable.Range(1, 100)
        .Where(x => x % 2 == 1)
        .Aggregate((x, y) => x + y))

Specifically:

  • blocks: {||} - become lambdas: =>
  • select becomes Where
  • inject becomes Aggregate
  • into becomes a direct call on a lambda instance
Pavel Minaev
Very nice except it misses the point of the Thrush combinator which in this case is to move the x*x to the end for readability. Worth a +1 anyway.
Jonas Elfström
Didn't realise that to be the case - I just looked at Scala translation, which does the same thing I did. That said, `.Into()` could be trivially written as an extension method if wanted.
Pavel Minaev
Ghosh's Scala implementation is very nice:(1 to 100) .filter(_ % 2 != 0) .foldLeft(0)(_ + _) .into((x: Int) => x * x)
Jonas Elfström
Yup, placeholders simplify things a bit. Actually, come to think of it, C# has operator overloading, so I think it would be possible to define `_` and overload operators for it in such a way that it would produce lambdas... it wouldn't let you write `_.foo()` like Scala does, but it would let you write `_ + 1` or even `_ + _`. I'll need to think about it.
Pavel Minaev