tags:

views:

1142

answers:

4

Hi, I haven't used a statically typed language in many years and have set myself the task of getting up to speed with C#. I'm using my usual trick of following the fifteen exercises here http://www.jobsnake.com/seek/articles/index.cgi?openarticle&8533 as my first task.

I've just finished the second Fibonacci task which didn't take to long and works just fine but in my opinion looks ugly and I'm sure could be achieved in far fewer lines of more elegant code.

I usually like to learn by pair programming with someone who already knows what they're doing, but that option isn't open to me today, so I'm hoping posting here will be the next best thing.

So to all the C# Jedi's out there, if you were going to refactor the code below, what would it look like?

using System;
using System.Collections;

namespace Exercises
{
 class MainClass
 {
  public static void Main(string[] args)
  {
   Console.WriteLine("Find all fibinacci numbers between:");
   int from = Convert.ToInt32(Console.ReadLine());
   Console.WriteLine("And:");
   int to = Convert.ToInt32(Console.ReadLine());
   Fibonacci fibonacci = new Fibonacci();
   fibonacci.PrintArrayList(fibonacci.Between(from, to));

  }
 }

 class Fibonacci
 { 
  public ArrayList Between(int from, int to)
  {    
   int last = 1;
   int penultimate = 0;
   ArrayList results = new ArrayList();
   results.Add(penultimate);
   results.Add(last);

   while(last<to)
   {
    int fib = last + penultimate;
    penultimate = last;
    last = fib;
    if (fib>from && fib<to) results.Add(fib.ToString());
   }
   return results;
  }

  public void PrintArrayList(ArrayList arrayList)
  {
   Console.WriteLine("Your Fibonacci sequence:");
   Console.Write(arrayList[0]);
   for(int i = 1; i<arrayList.Count; i++)
   {
    Console.Write("," + arrayList[i]);
   }
   Console.WriteLine("");
  }

 }
}

Regards,

Chris

+34  A: 

As an iterator block:

using System;
using System.Collections.Generic;
using System.Linq;

static class Program {
    static IEnumerable<long> Fibonacci() {
        long n = 0, m = 1;

        yield return 0;
        yield return 1;
        while (true) {
            long tmp = n + m;
            n = m;
            m = tmp;
            yield return m;
        }
    }

    static void Main() {
        foreach (long i in Fibonacci().Take(10)) {
            Console.WriteLine(i);
        }
    }
}

This is now fully lazy, and using LINQ's Skip/Take etc allows you to control the start/end easily. For example, for your "between" query:

foreach (long i in Fibonacci().SkipWhile(x=>x < from).TakeWhile(x=>x <= to)) {...}
Marc Gravell
Is there a Jedi badge in SO?
AnthonyWJones
'fraid not... there is "guru", but I haven't got it ;-p
Marc Gravell
Of course, there are links for creating your own SO badges...
Marc Gravell
Thanks Marc, that gave me exactly what I was looking for, and I now have new things to go away and get up to speed on.Anyone else have any alternative ways to solve the same problem?
ChrisInCambo
Well, you could use an array or List<T> rather than ArrayList, but either of these involves buffering, which the iterator block avoids.
Marc Gravell
By the way, you can learn all about iterator blocks in one of the (free) sample chapters of Jon Skeet's book "C# in Depth"; chapter 6: http://manning.com/skeet/
Marc Gravell
Then go and buy the rest of the book ;-p It really will help you understand what C# is about "under the covers".
Marc Gravell
Thanks for the tips, I'll check out the free chapter. Marc which packages to I need to use in order to make your example compile? At the moment the compiler is choking on the Take extension method.
ChrisInCambo
It needs a reference to System.Core.dll, and a "using System.Linq;" at the top of the file.
Marc Gravell
This is really a good solution!
BobbyShaftoe
A: 

For those who haven't yielded to Linq-ed in like me, the 'Simple Jack' Version. i'm SO banned out of the Jedi club ;)

static List<int> GetAllFibonacciNumbersUpto(int y)
{
   List<int> theFibonacciSeq = new List<int>();

   theFibonacciSeq.Add(0);   theFibonacciSeq.Add(1);

   int F_of_n_minus_2 = 0;   int F_of_n_minus_1 = 1;
   while (F_of_n_minus_2 <= y)
   {
      theFibonacciSeq.Add(F_of_n_minus_1 + F_of_n_minus_2);

      F_of_n_minus_2 = F_of_n_minus_1;
      F_of_n_minus_1 = theFibonacciSeq.Last<int>();
   }
   return theFibonacciSeq;
}

now that we have that out of the way...

// read in some limits
int x = 0; int y = 6785;

foreach (int iNumber in GetAllFibonacciNumbersUpto(y).FindAll(element => (element >= x) && (element <= y)))
    Console.Write(iNumber + ",");
Console.WriteLine();
Gishu
Except you're using Last(), a LINQ extension method... to be non-LINQ, you'd have to use list[list.Count-1]
Marc Gravell
Subjective: the only gripe i have with LINQ is that it makes the code unreadable . Last(), FindAll() - i can guess what it does.. but I had to go look up MSDN to know what Take() and TakeWhile() does..
Gishu
it may all be due to the fact that I'm not down with LINQ yet.. FWIW I did upvote your answer. its clean and concise.. albeit a little complex.
Gishu
For me as someone whose an experienced programmer but very new to C#, this is by far the most readable and fewer lines than the accepted answer.
ChrisInCambo
@ChrisInCambo - it is only fewer lines because I chose to include usage and the full `Program.cs` etc. Readability is subjective, but iterator blocks have a *lot* of advantages for infinite series like this. But yes, we could happily debate this one all day ;-p
Marc Gravell
@Gishu - it really is worth exploring the standard LINQ methods; things like Take() are *very* powerful - and Take/Skip are very common due to their use in paging (i.e. page 2, 10 items to a page is Skip(10).Take(10)
Marc Gravell
+2  A: 

If you prefer recursion instead of the loop:

public static void Main(string[] args)
{
    Func<int, int> fib = null;
    fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;

    int start = 1;
    int end = 10;
    var numbers = Enumerable.Range(start, end).Select(fib);
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}
Paco
Just my 2 cents... recursion is wasteful in case of computing fibonacci numbers
Gishu
It is, but what matters? It's cooler.
Paco
Functional programming languages do not use loops.
Paco
I didn't know that FProgs dont have loops... strange.
Gishu
When using recursive anonymous functions, use y-combinators!^^
Dario
+1  A: 

I would change the IEnumerable<int> type to IEnumerable<Int64> as it will start to overflow from 50

Can Erten
I think you want to change IEnumerable<int> to IEnumerable<long> ?
tuinstoel
tow-may-toe, toe-mar-toe
Garry Shutler