views:

147

answers:

5

Let's say you have the following code:

  string encoded="9,8,5,4,9";

  // Parse the encoded string into a collection of numbers
  var nums=from string s in encoded.Split(',')
           select int.Parse(s);

That's easy, but what if I want to apply a lambda expression to s in the select, but still keep this as a declarative query expression, in other words:

  string encoded="9,8,5,4,9";

  // Parse the encoded string into a collection of numbers
  var nums=from string s in encoded.Split(',')
           select (s => {/* do something more complex with s and return an int */});

This of course does not compile. But, how can I get a lambda in there without switching this to fluent syntax.

Update: Thanks to guidance from StriplingWarrior, I have a convoluted but compilable solution:

var result=from string s in test.Split(',')
           select ((Func<int>) 
             (() => {string u="1"+s+"2"; return int.Parse(u);}))();

The key is in the cast to a Func<string,int> followed by evaluation of the lambda for each iteration of the select with (s). Can anyone come up with anything simpler (i.e., without the cast to Func followed by its evaluation or perhaps something less verbose that achieves the same end result while maintaining the query expression syntax)?

Note: The lambda content above is trivial and exemplary in nature. Please don't change it.

Update 2: Yes, it's me, crazy Mike, back with an alternate (prettier?) solution to this:

public static class Lambda
{
  public static U Wrap<U>(Func<U> f)
  {
    return f();
  }
}
... 
  // Then in some function, in some class, in a galaxy far far away:

  // Look what we can do with no casts
  var res=from string s in test.Split(',')
          select Lambda.Wrap(() => {string u="1"+s+"2"; return int.Parse(u);});

I think this solves the problem without the ugly cast and parenarrhea. Is something like the Lambda.Wrap generic method already present somewhere in the .NET 4.0 Framework, so that I do not have to reinvent the wheel? Not to overburden this discussion, I have moved this point into its own question: Does this "Wrap" generic method exist in .NET 4.0.

+2  A: 

Assuming you're using LINQ to Objects, you could just use a helper method:

select DoSomethingComplex(s)

If you don't like methods, you could use a Func:

Func<string, string> f = s => { Console.WriteLine(s); return s; };
var q = from string s in new[]{"1","2"}
        select f(s);

Or if you're completely hell-bent on putting it inline, you could do something like this:

from string s in new[]{"1","2"}
select ((Func<string>)(() => { Console.WriteLine(s); return s; }))()
StriplingWarrior
By using {} braces, you're effectively saying that your lambda expression represents a function.
StriplingWarrior
I think you're on to the answer. The key is in the cast to Func<int> (not string) of the lambda expression. Let me play with it a little more, but your answer is on the right track, so +1 for now...
Michael Goldshteyn
Yes, the T in `Func<T>` needs to be whatever type you expect to *get* from the expression. His example uses `string` because he's simply returning `s` at the end of the day.
Anthony Pegram
A: 

You could simply do:

var nums = from string s in encoded.Split(',')
           select (s => { DoSomething(); return aValueBasedOnS; });

The return tells the compiler the type of the resulting collection.

Johann Blais
I tried something similar to this, but it will not compile: var nums=from string s in test.Split('_') select {t => {int n=DoSomething(t); ... return n;}}; // Where DoSomething is defined as: int DoSomething(string s) ...
Michael Goldshteyn
Correction to above expression (still won't compile): var result=from string s in test.Split('_') select (t => {return int.Parse(DoSomething(t));});
Michael Goldshteyn
A: 

How about this:

var nums= (from string s in encoded.Split(',') select s).Select( W => ...);
Vivek
A: 

Can anyone come up with anything simpler?

Yes. First, you could rewrite it like this

var result = from s in encoded.Split(',')
             select ((Func<int>)(() => int.Parse("1" + s + "2")))();

However, that's not really readable, particularly for a query expression. For this particular query and projection, the let keyword could be used.

var result = from s in encoded.Split(',')
             let t = "1" + s + "2"
             select int.Parse(t);
Anthony Pegram
I don't mind the let in between, but how do I get a lambda expression in there, not "1"+s+"2" . That was just an example...
Michael Goldshteyn
The same way you would for the `Select`. Such as `let t = ((Func<string>)(() =>"1" + s + "2"))()`. Again, it's back in the realm of marginally unreadable, and moreso if you intend to do more. I once again say your question/comments make me nervous as to what your *real* intent is.
Anthony Pegram
@Michael, the bottom line is that when using query expression syntax, you simply need to tell the compiler about your lambda by casting to whatever `Func<>` is applicable for the expression. You will further need to invoke the expression with however many parameters are appropriate. But I would simply like to say for the record: Be very cautious.
Anthony Pegram
OK, going through the cast and execute basically makes the extra t (and let) superfluous and were back to StriplingWarrior's original solution.
Michael Goldshteyn
A: 

IEnumerable integers = encoded.Split(',').Select(s => int.Parse(s));

Edit:

IEnumerable<int> integers = from s in encoded.Split(',') select int.Parse(string.Format("1{0}2",s));
Seattle Leonard
I think you missed the point of the question, which was to avoid calling `.Select(...)`
StriplingWarrior