1

Ciao everyone, i was finally able to switch from .NET Framework 2.0 to .NET Framework 4.0.

I am trying to get some experience about lambda expression and LINQ.

Is it possible to translate this:

for (int cont = 0;cont < args.Length; cont++)
    Console.WriteLine("#" + cont + " " + "<" + args + ">");

into a single line lambda expression,or using LINQ, or even some other costruct i am no aware of ? Thank you in advance.

Yahia
  • 69,653
  • 9
  • 115
  • 144
Duncan_McCloud
  • 543
  • 9
  • 24
  • What are you tryng to do with `"<" + args` part? Args is a string[] array I think, correct? – Marco Oct 25 '11 at 07:38
  • I think in this example your code is the best solution to write every item to the console. :) But you can use LINQ to query the args collection if you wanna do something else with the items. – BigL Oct 25 '11 at 07:41
  • I think you want `args[cont]` instead of `args` in your second line. – CodesInChaos Oct 25 '11 at 07:42
  • args[] is the input string[] of a console application. In the log of the application i wantto write something like: #1 #2 #3 ... I thought there was an easy one line expression sintax for it – Duncan_McCloud Oct 25 '11 at 07:45
  • 1
    It should be `args[cont]` then, if args is an array. – xanatos Oct 25 '11 at 08:02
  • 1
    like xanatos and Etienne say, don't use LINQ just because its possible and new, a normal `for` or `foreach` will likely have equal or better performance here and more importantly is simple to understand. – Jodrell Oct 25 '11 at 08:09

8 Answers8

6

LINQ operations shouldn't have side effects or do actions. They should only be used to produce, from a IEnumerable<T> another IEnumerable<T> (or from a IQueryable<T> another IQueryable<T> or IEnumerable<T>) (or if aggregating, like .Max, .All... a single result)

Read for example http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx.

(and note that this question is quite asked on SO... Read for example my reply here ToList().ForEach in Linq and see a solution to a similar problem)

Now, if you hate yourself enough, you can do this:

strs.Select((p, index) =>
{
    Console.WriteLine("#" + index + " " + "<" + p + ">");
    return true;
}).All(p => p);

We are using the .All(p => p) to "materialize" the Select operation. This because Select is "lazy" and won't be executed if no one cycles it.

Technically you could even try to obfuscate it a little:

strs.Select((p, index) =>
{
    Console.WriteLine("#" + index + " " + "<" + p + ">");
    return false;
}).Any(p => p);

(All checks that all the elements are true and stops if one of the is false, so it has to cycle all of them (because we always return true;). Any (as written) checks that at least one of the elements is true and then stops (but all of our elements are false, becasue we do return false; so the Any will cycle all the elements)

Now... The advantage of these techniques over using ToList is that we aren't "duplicating" an Array to a List<T> just to use a "wrong" method of List<T>. If the ForEach method of List<T> is "wrong", duplicating an Array to a List just to use it is doubly-wrong.

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
4

There's something you must understand first. LINQ is a functional style API. It's main purpose is to provide an easy and concise way to manipulate sequences.

Your code could be written using LINQ, but it's not desirable: writing stuff to a stream (such as a console) is best done in imperative style, and your request smacks of golden hammer.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
3

I don't know why you would want this but:

   args.ToList().ForEach(i => Console.WriteLine("#{0} <{1}>",
        args.ToList().IndexOf(i),
        i));

I suppose you did want to output the item itself and not the whole list on every line.

And I've made the construction of the output somewhat nicer.

IMO: Your original code is faster and easier to read.

GvS
  • 52,015
  • 16
  • 101
  • 139
  • Thank you, i think you are right. Since those are my first step into lamba i just thought that there was a better solution. However VS2010 intellisense show the method ".ToList<>" at first. Shouldn't it shows two separate methods ".ToList<>" and ".ToList()" ? – Duncan_McCloud Oct 25 '11 at 07:52
  • 1
    The method is actually `.ToList()`, but the compiler can resolve the `` part, so you can leave it out. – GvS Oct 25 '11 at 08:03
1

Converting it to a lambda expression is trivial, and useless.

Action doLoop = ()=>{for (int cont = 0;cont < args.Length; cont++)
    Console.WriteLine("#" + cont + " " + "<" + args + ">");}

But if you want to replace the loop with linq, that's possible but a bad idea. Linq is designed for side effectless code, Console.WriteLine is a side effect.

If you want to use linq, but still keep the Console.WriteLine call you could write:

foreach(string s in Enumerable.Range(0,args.Length).Select("#" + cont + " " + "<" + args + ">"))
    Console.WriteLine(s);

It would be possible to abuse Select to entirely eliminate the loop, but that's too ugly to post here.

Many people have defined a ForEach extension method with which the code becomes:

Enumerable.Range(0,args.Count).ForEach(()=>Console.WriteLine("#" + cont + " " + "<" + args + ">"));

But I'm not fond of this style.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
1

I hope this links will help you to understand and learn Lambdas and Linq.

MSDN Lambda Expressions

101 LINQ Examples

BigL
  • 1,631
  • 1
  • 11
  • 10
1

This will do exactly what you've asked for:

    args
        .Select((x, n) => String.Format("#{0} <{1}>", n, args))
        .ToList()
        .ForEach(Console.WriteLine);

It produces the same as your code:

#0 <System.String[]>
#1 <System.String[]>
#2 <System.String[]>

I'm guessing you wanted something more like this:

var args = new [] { "Apple", "Banana", "Cherry", };

args
    .Select((x, n) => String.Format("#{0} <{1}>", n, x))
    .ToList()
    .ForEach(Console.WriteLine);

Which produces:

#0 <Apple>
#1 <Banana>
#2 <Cherry>
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

Assuming something like this:

string[] args = new string[] { "a", "pippo", "c" };

you can try this:

args.ToList().ForEach(
    p => Console.WriteLine(
        String.Format("# {0} <{1}>", p, args.Length)));
Marco
  • 56,740
  • 14
  • 129
  • 152
-1

your args must be an array:

Array.ForEach(args, s => { Console.WriteLine("#" + Array.IndexOf(args, s) + " " + "<" + args + ">"); });
ojlovecd
  • 4,812
  • 1
  • 20
  • 22
  • 1
    Not equivalent with the original code(doesn't work with duplicate items) and slower ( just `O(n^2)` instead of `O(n)`) – CodesInChaos Oct 25 '11 at 07:44