迭代中的努力——FizzBuzz
本文关键字:FizzBuzz 努力 迭代 | 更新日期: 2023-09-27 17:50:21
EDIT不管它的价值是什么,承认它可能不是那么多。我做了一个小测试来扩展这个问题。
我已经编写了两个函数来枚举FizzBuzz"系列"。"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));
}
,
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++;
}
}
我做了一点计时,在发布配置中编译,优化并从命令行运行。在10^8
迭代中,没有实际报告每个项目的开销,我得到的结果近似于,
简单:14.5秒
优化时间:10秒
您会注意到"优化"的函数更快,但更冗长。它的行为可以通过简单地改变其头部的常量来改变。
如果这看起来有点琐碎,我很抱歉。
考虑这个函数
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();
}
输出看起来像
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
...
有人能想出更好的方法吗?请同时考虑性能和可维护性
StringBuilder result = new StringBuilder();
对于一个固定的上限(100),我不会麻烦这个,但是好吧…
StringBuilder line = new StringBuilder();
但是这个StringBuilder
不仅是多余的,它真的是低效的。我甚至不需要基准测试就知道这一点。
if (line.Length == 0)
这只是模糊了逻辑(这应该实现"fizzbuzz"问题,对吗?)使逻辑显式。
请同时考虑性能和可维护性
这是错误的方向。可维护性第一,性能第二(如果有的话)。你的代码实际上效率很低,但这无关紧要:有100次迭代-性能根本不重要。
此外,这段代码的可维护性开销是多少?这是一个固定规格的玩具样品。不存在可维护性问题。我甚至不需要花心思在这里,Linq会自动解决这个问题:
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()));
但我同意,如果一个人不习惯Aggregate
函数,这可能会影响可读性。让它更明确:
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();
其他都是过度设计。
假设您的代码只是您想要实现的示例…创建更少的stringbuilder的建议:
{
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);
}
}
如果我们让事情变得更困难一些呢?1)不允许除法、取模;2)循环必须跳过所有不必要的迭代。下面是答案:
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;
}