3
\$\begingroup\$

I have recently been asked to do a test prior to an interview to test my C# skills. I am fairly new to C# (6 months) and I think this showed with me producing an overly complex answer to their questions.

I will post the 3 questions below as well as my answers. Please can someone give me a better solution to this. I had thought about using Linq as well to do this, but again wasn't really sure how. I also tried to add delegates into the mix, but not sure if I should have done this or not. Also I wasn't sure what tests to do in my unit testing, again any quick examples would be very appreciated.

Question 1

Write a program that prints the numbers from 1 to 100. For multiples of three print "Three" and for the multiples of five print "Five" and for numbers which are multiples of both three and five print "ThreeFive".

Question 2

If we list all the whole numbers up to 15 that are multiples of 3 or 5, we get 3, 5, 6 and 9, 10, 12, 15. The sum of these multiples is 60. Find the sum of all the multiples of 3 or 5 up to 1000.

Question 3

Calculate the sum of all the even numbers in the Fibonacci sequence (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, etc.) where each number in the sequence represents the sum of the previous two, up to 4,000,000.

Bonus Points

  • Unit tests your solutions
  • Bases the answer to 1 and 2 on the same 'number generator'

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestPaperApp
{
    public class TestPaperTestQuestions
    {
        public static List<string> TestPaperList = new List<string>();
        public static List<int> SumItUpList = new List<int>();
        public static List<int> FibonacciList = new List<int>();
        public static string range;
        public delegate void Question(int number);

        public static void Main(string[] args)
        {

            // Write numbers 1-100 - multiples of 3 write 'Three' multiples of 5 print ‘Five’ multiples of both print 'ThreeFive'
            QuestionOne(1, 100);
            //Sum the numbers between 1-1000 which are divisible by 3 or 5
            QuestionTwo(1, 1000);
            //Sum the even Fibonacci numbers less than 4,000,000
            QuestionThree(4000000);
        }

        public static void GenerateSequence(int startNumber, int finishNumber, Question Quest)
        {
            //Clear the lists for each time the sequence is ran
            SumItUpList.Clear();
            TestPaperList.Clear();

            //look from the start number to finish number and add the relevent values to the different lists
            for (int i = startNumber; i <= finishNumber; i++)
            {
                //Use delegate to make number generator reusable
                Quest(i);
                // get the range of the numbers you are looping through to add to the title labels
                range = startNumber.ToString() + "-" + finishNumber.ToString();
            }
        }

        public static void TestPaperListAdd(int i)
        {
            if (i % 3 == 0 && i % 5 == 0)
            {
                TestPaperList.Add("ThreeFive");
            }
            else if (i % 3 == 0)
            {
                TestPaperList.Add("Three");
            }
            else if (i % 5 == 0)
            {
                TestPaperList.Add("Five");
            }
            else
            {
                TestPaperList.Add(i.ToString());
            }
        }

        public static void SumItUpListAdd(int i)
        {
            if (i % 3 == 0 || i % 5 == 0)
            {
                SumItUpList.Add(i);
            }
        }

        public static List<String> QuestionOne(int start, int finish)
        {
            //Run the generate sequence method
            GenerateSequence(start, finish, TestPaperListAdd);
            Console.WriteLine("--- Write the numbers between " + range + " if the number is a multiples of 3 write 'Three'," +
    "a multiple of 5 print 'Five' and a multiple of both print 'ThreeFive'  ---");
            //Loop through all values and write to console window
            for (int i = 0; i < TestPaperList.Count(); i++)
            {
                Console.WriteLine(TestPaperList[i]);
            }
            return TestPaperList;
        }

        public static int QuestionTwo(int start, int finish)
        {
            //Run the generate sequence method
            GenerateSequence(start, finish, SumItUpListAdd);
            int sum = SumItUpList.Sum();
            Console.WriteLine("--- Sum of all numbers between " + range + " divisible by 3 or 5 ---");
            //Display sum of values in the console window
            Console.WriteLine(sum);
            return sum;
        }

        public static int QuestionThree(int finishNumber)
        {
            //Clear the Fibonacci list
            FibonacciList.Clear();

            //Add the first two values in the Fibonacci seequence
            FibonacciList.Add(0);
            FibonacciList.Add(1);
            //initiate twom integer values
            int highest = 0;
            int sumeven = 0;
            //loop through while the maximum Fibonacci number is less than the value selected for 'finish number'
            while (highest < finishNumber)
            {
                FibonacciList.Add(highest);
                highest = FibonacciList[FibonacciList.Count - 1] + FibonacciList[FibonacciList.Count - 2];
            }

            //Work out whether the number is even or not, if so add it to the sum
            for (int i = 0; i < FibonacciList.Count; i++)
            {
                if (FibonacciList[i] % 2 == 0)
                {
                    sumeven = sumeven + FibonacciList[i];
                }
            }
            //Write the sum of even Fibonacci numbers
            Console.WriteLine("--- Sum the even Fibonacci numbers less than " + finishNumber + " ---");
            Console.WriteLine(sumeven);

            return sumeven;
        }
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ For question 1, you might want to take a look at the many fizzbuzz implementations and reviews on this site ;) \$\endgroup\$ – Mathieu Guindon May 15 '14 at 18:47
  • 1
    \$\begingroup\$ you might want to have each question(code that goes with it) reviewed separately. \$\endgroup\$ – Malachi May 15 '14 at 19:02
  • \$\begingroup\$ Thank you for your responses, much nicer and more helpful than when I posted on stackoverflow (serves me right for posting on the wrong site I guess). I added them all as the bonus question asks for 1 and 2 in the same generator. I could perhaps just do those two and then the Fibonacci one separate \$\endgroup\$ – user3284707 May 15 '14 at 19:05
4
\$\begingroup\$

Just some general remarks:

  1. Question 1 is just a derivative of the classic FizzBuzz but using different words as mentioned in the comments.

  2. None of the questions actually require you to store all the number. You are wasting a lot of space but storing them all and then post-processing them - you could pretty much do all of it on the fly.

Some basic ideas for 1 and 2 using enumerators:

public IEnumerable<Tuple<int, bool, string>> GenerateFizzBuzzLikeSequence(int start, int end)
{
    for (int i = start; i <= end; ++i)
    {
        if (i % 3 == 0 && i % 5 == 0)
        {
            yield return Tuple.Create(i, true, "ThreeFive");
        }
        if (i % 3 == 0)
        {
            yield return Tuple.Create(i, true, "Three");
        }
        if (i % 5 == 0)
        {
            yield return Tuple.Create(i, true, "Five");
        }
        yield return Tuple.Create(i, false, i.ToString());
    }
}

Now you have a generator which creates a sequence yielding for each number if it's a multiple of 3 or 5 and the translated version of it. You can create algorithms to implement question 1 and 2 based on it.

public void Question1()
{
    foreach (var item in GenerateFizzBuzzLikeSequence(1, 100))
    {
        Console.WriteLine(item.Item3);
    }
}

public void Question2()
{
    int sumOfAllMultipleOf3And5 = GenerateFizzBuzzLikeSequence(1, 1000).Where(t => t.Item2).Sum();
    Console.WriteLine(sumOfAllMultipleOf3And5);
}

Rather than using a Tuple a little helper class could be created with somewhat more meaningful property names.

Similar you can write a generator for the Fibonacci sequence returning all the Fibonacci numbers and then you sum all the even ones (special handling for max < 2 not included):

public IEnumerable<int> GenerateFibonacciSequence(int max)
{
    int a = 0;
    int b = 1;
    yield return a;
    yield return b;
    for (int i = 2; i <= max; ++i)
    {
        int number = a + b;
        yield return number;
        a = b;
        b = number;
    }
}
\$\endgroup\$
  • \$\begingroup\$ thank you so much for taking the time to write this detailed answer above. This has really helped with my understanding. I'll have a go today to try and do this myself using your guidance :-D \$\endgroup\$ – user3284707 May 16 '14 at 5:28
3
\$\begingroup\$

How to define recursive IEnumerables

Chris's answer for the fibonacci question is quite clear and very efficient (especially the constant space part), but the max limit was arbitrary. In fact if we defined Fibonacci sequence as an infinite enumerable we could write fibs.TakeWhile(x => x < 4000000).

Of course, leaving efficiency aside, I wanted to see the recursive nature of the sequence. I normally would like to write something like this:

var fibs = new []{1, 1}.Concat(fibs.Zip(fibs.Skip(1), (a, b) => a + b)));

which of course does not compile.

I was intrigued to find out how could one define recursive IEnumerables. I could not find an example by googleing, so I decided to find a way myself:

var fibs = Y(_fibs => new []{1, 1}.Concat(_fibs.Zip(_fibs.Skip(1), (a, b) => a + b)));

Where Y is a fixed-point finder for recursive relations over enumerables. It is analogous to the fixed-point operators used to write recursive lambda expressions. It is implemented thus:

static IEnumerable<T> Y<T>(Func<IEnumerable<T>,IEnumerable<T>> f) {
    var knownValues = new List<T>();

    while(true) 
    {
        T next = f(knownValues).Skip(knownValues.Count).First();
        yield return next;
        knownValues.Add(next);
    }
}

It is now straightforward to write the necessary query:

Console.WriteLine(fibs.TakeWhile(x => x < 4000000).Where(x => x % 2 == 0).Sum());
\$\endgroup\$
0
\$\begingroup\$

I've had a go looking at the examples above and come up with the following. I've created a helper class as Chris said instead of using a Tuple in his example and changed the fibinacci code a little to use a while loop instead of a for loop.

I'm afraid I didn't really understand Abuzittin's post above, but can see he has used a similar lambda expression in his answer to mine at the end.

If anyone has any comments on the below i'd love to hear them.

public class TestPaper
{

    public static void Main(string[] args)
    {
        TestPaper test = new TestPaper();
        //Question1
        foreach (var item in test.GenerateSequence(1, 100))
        {
            Console.WriteLine(item.Fizzbuzz);
        }
        //Question2
        Console.WriteLine(test.GenerateSequence(1, 1000).Where(n => n.Isfizzbuzz).Sum(n => n.Number));
        //Question3
        Console.WriteLine(test.fibinacci(4000000).Where(n => n % 2 == 0).Sum());
    }

    public IEnumerable<Helper> GenerateSequence(int start, int finish)
    {
        for (int i = start; i <= finish; i++)
        {
            if (i % 15 == 0)
            {
                yield return new Helper(i, true, "Fizzbuzz");
            }
            else if (i % 3 == 0)
            {
                yield return new Helper(i, true, "Fizz");
            }
            else if (i % 5 == 0)
            {
                yield return new Helper(i, true, "Buzz");
            }
            else
            {
                yield return new Helper(i, false, i.ToString());
            }
        }

    }

    public IEnumerable<int> fibinacci(int Max)
    {
        int a = 0;
        int b = 1;
        yield return a;
        yield return b;

        while (b < Max)
        {
            int c = a + b;
            a = b;
            b = c;
            yield return c;
        }
    }

    public class Helper
    {
        public int Number { get; set; }
        public bool Isfizzbuzz { get; set; }
        public string Fizzbuzz { get; set; }

        public Helper(int number, bool isfizzbuzz, string fizzbuzz)
        {
            Number = number;
            Isfizzbuzz = isfizzbuzz;
            Fizzbuzz = fizzbuzz;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ If you'd like to get a follow up review on your changed code you should submit it as a separate question \$\endgroup\$ – ChrisWue Feb 10 '15 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.