Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / February 2008

Tip: Looking for answers? Try searching our database.

Inefficient? but fun. :-)

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
jehugaleahsa@gmail.com - 25 Feb 2008 22:57 GMT
public static IEnumerable<T> RotateLeft<T>(IEnumerable<T>
collection)
       {
           using (IEnumerator<T> values = collection.GetEnumerator())
           {
               if (values.MoveNext())
               {
                   T last = values.Current;
                   while (values.MoveNext())
                   {
                       yield return values.Current;
                   }
                   yield return last;
               }
           }
       }

public static IEnumerable<T> RotateLeft<T>(IEnumerable<T> collection,
int toTheLeft)
{
   if (toTheLeft < 0)
   {
       throw new ArgumentException("No negatives please");
   }
   IEnumerable<T> result = collection;
   for (int i = 0; i != toTheLeft; ++i)
   {
       result = RotateLeft<T>(result);
   }
   return result;
}
Jon Skeet [C# MVP] - 25 Feb 2008 23:10 GMT
>        public static IEnumerable<T> RotateLeft<T>(IEnumerable<T>
> collection)
[quoted text clipped - 27 lines]
>     return result;
> }

Here's an alternative, for .NET 3.5:

public static IEnumerable<T> RotateLeft<T>(IEnumerable<T> collection)
{
   return RotateLeft(collection, 1);
}

public static IEnumerable<T> RotateLeft<T>(IEnumerable<T> collection,
                                          int places)
{
   return collection.Skip(places).Concat(collection.Take(places));
}

Note that these could also be extension methods...

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Peter Duniho - 26 Feb 2008 00:36 GMT
> Here's an alternative, for .NET 3.5:
>
[quoted text clipped - 8 lines]
>     return collection.Skip(places).Concat(collection.Take(places));
> }

And since in 3.5 you get "Count()", it's simple enough to extend the idea  
to handle left and right rotations:

    public static IEnumerable<T> Rotate<T>(IEnumerable<T> collection, int  
places)
    {
        if (places < 0)
        {
            places += collection.Count();
        }

        return collection.Skip(places).Concat(collection.Take(places));
    }

Where "places" is assumed to be within range, of course (I figure that  
since that seemed to be an assumption in your version, I'm allowed to make  
the same assumption :)...note that the original handles out-of-range input  
just fine, if inefficiently).

Though, one thing I'm wondering about is the efficiency of the Count()  
method.  Except for the 1-place rotate, the above would still always at  
least as efficient as the originally-proposed "rotate by 1, once for each  
place" approach.  But it's tempting to throw in another call to Count() to  
deal with out-of-range values of "place" (by using % to bring it back into  
range), and if it's going to keep enumerating the entire collection each  
time you call Count(), that would be nice to know (so you could cache the  
result).

Even barring 3.5, the original need not be so inefficient.  Here's a  
version that does only left-shifts without all the repeated enumerations  
(has the same assumption that the specified rotation is within range):

        public static IEnumerable<T> RotateLeft<T>(IEnumerable<T>  
collection, int crotate)
        {
            using (IEnumerator<T> enumerator = collection.GetEnumerator())
            {
                T[] rgt = new T[crotate];

                for (int irotate = 0; irotate < crotate; irotate++)
                {
                    enumerator.MoveNext()
                    rgt[irotate] = enumerator.Current;
                }

                while (enumerator.MoveNext())
                {
                    yield return enumerator.Current;
                }

                foreach (T t in rgt)
                {
                    yield return t;
                }
            }
        }

The above also doesn't deal with out-of-range input, but that's just for  
the purpose of illustration.  It wouldn't be difficult to detect hitting  
the end of the collection in the initial for() loop and handling that  
appropriately (i.e. at that point, you now know how large the collection  
is and can calculate the size-constrained rotation that is equivalent to  
the asked-for one; if handling that case one would probably want to use a  
List<T> instead of a T[] to store the intermediate result as well, to  
avoid allocating an arbitrarily huge array).

Also, noting that the non-3.5 versions rely heavily on the custom  
enumerator syntax, avoiding allocation of a lot of extra data, at least  
for small rotates to the left.  The original has no real memory bounding  
issues, and my version above is fine as long as the number of rotations  
doesn't get too large (and frankly, if it's large enough to cause an  
out-of-memory problem, I suspect that the nested iteration approach would  
cause a "this algorithm can't finish in a reasonable amount of time"  
problem :) ).

How does the 3.5 syntax handle this?  Will it internally allocate a copy  
or copies of the collection for the purpose of providing the various  
intermediate results?  Or is there something behind the scenes that is  
essentially equivalent to these custom enumerator approaches?

And finally, how does this relate to the article/web page you posted  
awhile back regarding custom enumerators attached to asynchronous i/o?  
The two non-3.5 versions here only enumerate through the collection once;  
does the 3.5 version break if you've got an enumerator that is only valid  
for a single enumeration?

Pete
Jon Skeet [C# MVP] - 26 Feb 2008 00:53 GMT
<snip>

> Also, noting that the non-3.5 versions rely heavily on the custom
> enumerator syntax, avoiding allocation of a lot of extra data, at least
[quoted text clipped - 9 lines]
> intermediate results?  Or is there something behind the scenes that is
> essentially equivalent to these custom enumerator approaches?

It will call GetEnumerator() twice - there's no actual need for
buffering here. Skip() will just call MoveNext() as many times as it
needs to without using the data; Take() will call MoveNext() as many
times as it needs to and then stop.

> And finally, how does this relate to the article/web page you posted
> awhile back regarding custom enumerators attached to asynchronous i/o?
> The two non-3.5 versions here only enumerate through the collection once;
> does the 3.5 version break if you've got an enumerator that is only valid
> for a single enumeration?

Yes, it would. On the other hand, it copes with the situation where the
rotation size is massive. Swings and roundabouts, as is so often the
case...

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Peter Duniho - 26 Feb 2008 01:24 GMT
> [...]
> It will call GetEnumerator() twice - there's no actual need for
> buffering here. Skip() will just call MoveNext() as many times as it
> needs to without using the data; Take() will call MoveNext() as many
> times as it needs to and then stop.

And I infer from the statement that "there's no actual need for buffering  
here" that the Concat() method sequences through the two enumerators as  
appropriate as the returned enumerator is actually processed?

>> And finally, how does this relate to the article/web page you posted
>> awhile back regarding custom enumerators attached to asynchronous i/o?
[quoted text clipped - 7 lines]
> rotation size is massive. Swings and roundabouts, as is so often the
> case...

Cool.  Thanks.

Pete
Jon Skeet [C# MVP] - 26 Feb 2008 07:46 GMT
> > [...]
> > It will call GetEnumerator() twice - there's no actual need for
[quoted text clipped - 5 lines]
> here" that the Concat() method sequences through the two enumerators as  
> appropriate as the returned enumerator is actually processed?

Yes. Concat uses deferred execution.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.