开发者

Efforts in iteration - FizzBuzz

EDIT For what its worth, which admittedley may not be that much. I've done a little test to expand this question.

I've written two functions to enumerate the FizzBuzz "series."

private static IEnumerable<string> SimpleFizzBuzz(
        int start = 0,
        int end = int.MaxValue)
{
    return Enumerable.Range(start, end).Select(i =>
        i % 15 == 0 ? "fizzbuzz" :
        i % 3 == 0 ? "fizz" :
        i % 5 == 0 ? "buzz" :
        i.ToString(CultureInfo.InvariantCulture));
}

and,

private static IEnumerable<string> OptimizedFizzBuzz(
        int start = 0,
        int end = int.MaxValue)
{
    const int fizz = 3;
    cons开发者_JAVA百科t int buzz = 5;
    const string fizzString = "fizz";
    const string buzzString = "buzz";
    const string fizzBuzzString = fizzString + buzzString;

    var fizzer = start % fizz;
    var buzzer = start % buzz;

    if (fizzer == 0)
    {
        fizzer = fizz;
    }

    if (buzzer == 0)
    {
        buzzer = buzz;
    }

    for (var i = start; i <= end; i++)
    {
        if (buzzer == buzz)
        {
            if (fizzer == fizz)
            {
                yield return fizzBuzzString;
                buzzer = 1;
                fizzer = 1;
                continue;
            }

            yield return buzzString;
            buzzer = 1;
            fizzer++;
            continue;
        }

        if (fizzer == fizz)
        {
            yield return fizzString;
            buzzer++;
            fizzer = 1;
            continue;
        }

        yield return i.ToString(CultureInfo.InvariantCulture);
        fizzer++;
        buzzer++;
    }
}

I've done a little timing, compiled in Release configuration, with optimizations and run from the command line. Over 10^8 iterations, without the overhead of actually reporting each item, I get results that approximate to,

Simple: 14.5 Seconds

Optimized: 10 Seconds

You'll note that the "optimized" function is faster but more verbose. It's behaviour can be altered simply by changing the constants at its head.


Apologies if this seems a little trivial.

Consider this function.

using System.Text;

public string FizzBanger(int bound)
{
    StringBuilder result = new StringBuilder();
    for (int i = 1; i < bound; i++)
    {
        String line = String.Empty;
        if (i % 3 == 0) line += "fizz";
        if (i % 5 == 0) line += "buzz";
        if (String.IsNullOrEmpty(line)) line = i.ToString();
        result.AppendLine(line.ToString());
    }
    return result.ToString();
}

The output will look like

1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
...

Can anybody think of a better way of doing it? Please consider both performance and maintainability.


StringBuilder result = new StringBuilder();

For a fixed upper bound (100) I wouldn’t bother with this but ok …

StringBuilder line = new StringBuilder();

But this StringBuilder isn’t only redundant, it’s really inefficient. And I don’t even need to benchmark to know this.

if (line.Length == 0)

And this just obscures the logic (this is supposed to implement the “fizzbuzz” problem, right?). Make the logic explicit.

Please consider both performance and maintainability.

That’s the wrong way round. Maintainability first, performance second (if at all). Your code is actually quite inefficient but that’s irrelevant: there are 100 iterations – performance doesn’t matter at all.

Furthermore, what maintainability overhead does this code have? It’s a toy sample with fixed specs. There are no maintainability issues. I wouldn’t even bother with anything fancy here, Linq solves this automagically:

return Enumerable.Range(1, bound - 1).Aggregate("",
    (accu, i) =>
        string.Format("{0}\n{1}", accu,
            i % 15 == 0 ? "fizzbuzz" :
            i % 3 == 0 ? "fizz" :
            i % 5 == 0 ? "buzz" : i.ToString()));

But I agree that this may strain readability if one isn’t used to the Aggregate function. So make it more explicit:

var result = new StringBuilder();
for (int i = 1; i < bound; i++)
    result.AppendLine(
        i % 15 == 0 ? "fizzbuzz" :
        i % 3 == 0 ? "fizz" :
        i % 5 == 0 ? "buzz" : i.ToString());
return result.ToString();

Everything else is over-engineering.


Assuming that your code is just an example of what you want to achieve... a proposal to create less StringBuilders:

{
      StringBuilder result = new StringBuilder();
      for (int i = 1; i < 101; i++)
      {
           var rest3 = i % 3;
           var rest5 = i % 5;

           if (rest3 == 0) result.Append("fizz");
           if (rest5 == 0) result.Append("bang");
           if (rest3 != 0 && rest5 != 0)
               result.Append(i);

           result.Append(System.Environment.NewLine);
      }
}


What if we make things a bit more difficult? 1) No division or modulo operations allowed; 2) The loop must skip all unnecessary iterations. Here is the answer:

int n3 = 3;
int n5 = 5;
int i = 3;
while (i <= 100)
{
    Console.Write(i.ToString() + " - ");

    if (i == n3)
    {
        Console.Write("fizz");

        n3 = n3 + 3;
    }

    if (i == n5)
    {
        Console.Write("buzz");

        n5 = n5 + 5;
    }

    Console.WriteLine();

    i = n3 < n5 ? n3 : n5;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜