Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lambda as default argument

Tags:

c#

I was looking for an anwer to question Get next N elements from enumerable didn't find any satisfying and brewed my own. What I came up with was

IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n, Func<IEnumerable<R>, T> action){
  IEnumerable<R> head;
  IEnumerable<R> tail = src;
  while (tail.Any())
  {
    head = tail.Take(n);
    tail = tail.Skip(n);
    yield return action(head);
  }
}

What I would really like though, is to have action have a default of t=>t, but I can't figure out how to make that a default argument. The signature IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n, Func<IEnumerable<R>, T> action = t=>t) gives a syntax error.

My question is, how do I do this?

I suppose this is identical to Specifying a lambda function as default argument but for C# instead of C++

As a side note, I know it doesn't make any syntactical difference, but would it be easier to read if I switched T and R?

like image 627
Martijn Avatar asked Jun 21 '12 14:06

Martijn


2 Answers

Default values have to be constants, and the only constant value for a delegate is a null reference.

I suggest you use overloading instead. Note that t => t wouldn't be valid anyway, unless you know that there's a conversion from IEnumerable<R> to T, which I don't see anywhere.

Aside from the lambda expression validity problem, you could use the null coalescing operator:

IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n,
                           Func<IEnumerable<R>, T> action = null)
{
    action = action ?? (t => t);
    ...
}

... but that would be sort of abusing it, and you wouldn't be able to tell whether the null was actually from a caller who thought they were passing a non-null value, and would prefer you to raise an ArgumentNullException.

EDIT: Additionally, note that your method is fundamentally problematic - iterating over the chunks will evaluate the original sequence several times (once per chunk) in order to skip the right amount. It would probably be better to write a method which eagerly read each chunk before returning it. We have a similar method in MoreLINQ, called Batch. Note that Batch has exactly the overload mentioned here - and in this case, the t => t works, because the overload has fewer type parameters, so we know the identity conversion is okay.

like image 87
Jon Skeet Avatar answered Sep 18 '22 01:09

Jon Skeet


It's the same for C#: create an overload.

IEnumerable<T> Chunk<T, R>(IEnumerable<R> src, int n){
    return Chunk<T, R>(src, n, t => t);
}
like image 45
Mattias Buelens Avatar answered Sep 22 '22 01:09

Mattias Buelens