Efforts in iteration - FizzBuzz

久未见 提交于 2019-12-01 12:49:58

问题


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;
    const 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.


回答1:


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.




回答2:


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);
      }
}



回答3:


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;
}


来源:https://stackoverflow.com/questions/5661269/efforts-in-iteration-fizzbuzz

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!