views:

162

answers:

3

OOC: Out Of Curiosity

So, as a little exercise and for the sake of learning, I decided to check if I was able to implement a very basic recursive function that would return a List<int>, but with the following restrictions:

1- The result should be returned by the function itself (as opposed to passed as an argument to a void function).

2 - No local "named" variables declared in the function's body.

I came up with the solution below (BTW: can this be improved in any way?)

While doing this, I learnt that ToList() is not the same thing as casting to List<T> (see example below) - Anyone out there who can explain what happens under the hood and what the difference between the two is?

Thanks!

PS - I'm using version 4.0 (in case it matters).

EDIT: the runtime error is Unable to cast object of type '<ConcatIterator>d__71'1[System.Int32]' to type 'System.Collections.Generic.List'1[System.Int32]'

public static List<int> SomeIntegers(int min, int max)
{
    //assume max >= min for simplicity  
    if (min == max)
        return new List<int>() { min };

    // runtime error 
    //return (List<int>)(SomeIntegers(min, max - 1).Concat(new List<int>() { max }));   

    //works
    return (SomeIntegers(min, max - 1).Concat(new List<int>() { max })).ToList(); 
}
+8  A: 

ToList is not the same as (casting) to List.

ToList takes any IEnumerable (lists, arrays, Dictionaries, Sets, etc) and turns it into a list.

Casting to List takes an object that is already a list of some sort, and labels it as a list. example:

// fail -- arrays are not lists
var not_a_list = (List<int>)int[];
// success: arrays *are* IEnumerable, so you can convert them to a list.
var list_from_array = new [] { 1,2,3,4,5 }.ToList();
// success: WorkflowRoleCollection derives from List<WorkflowRole>
var derived_from_list = (List<WorkflowRole>) new WorkflowRoleCollection();

In your case, Concat returns an IEnumerable, and not a List. Remember that it has to support generators (which are lazy-evaluated), so it doesn't make sense for it to be anything like a list underneath.

Btw, have you taken a look at the built-in function Enumerable.Range ?

Jimmy
Thanks, I didn't know about that function (would have saved me quite a few lines of code here and there), but the idea was not so much to build a list of integers as it was to have a recursive funtion itself return the collection (of whatever) and to avoid declaring variables in the function's body.
d.
+2  A: 
  • Casting only works if you actually have a List<T>, something derived from List<T>, or something with a valid type conversion to List<T>, otherwise it fails with an InvalidCastException. ToList() works on any IEnumerable.
  • ToList() always creates a new copy of the list even if you already have a list. Casting something to List<T> typically does not produce a copy of the list - it just gives you a new compile-time type for the same object.
Mark Byers
+2  A: 

By the way, the best way to generate a list of integers is:

Enumerable.Range(min, length);

or

Enumerable.Range(min, max-min+1);

But that doesn't help you learn, so kudos to you! :)

Michael Bray
Thanks, I just checked this; so it seems that we don't get an `Enumerable.Range(min, max, step)` or `Enumerable.Range(min, max, x => x*x)`, right?
d.
Not directly, but you could certainly do something like `Enumerable.Range(min, length).Where(x => (x-min)%step==0)` and `Enumerable.Range(min, length).Select(x => x*x);` to get the equivalent of those
Michael Bray
Also note that the second parameter on Enumerable.Range is LENGTH, not MAX
Michael Bray
I'm just checking this class `Enumerable`; this is great stuff, thanks a lot!
d.