迭代中的努力 - FizzBuzz
编辑 对于它的价值,承认可能没有那么多。我做了一些测试来扩展这个问题。
我编写了两个函数来枚举 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
...
有人能想到更好的方法吗?请同时考虑性能和可维护性。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
对于固定的上限(100),我不会为此烦恼,但好吧......
但这个 StringBuilder 不仅是多余的,而且效率很低。我什至不需要进行基准测试就知道这一点。
这只是模糊了逻辑(这应该是为了实现“fizzbuzz”问题,对吧?)。让逻辑明确。
这是错误的做法。可维护性第一,性能第二(如果有的话)。您的代码实际上效率很低,但这无关紧要:有 100 次迭代 - 性能根本不重要。
此外,这段代码有哪些可维护性开销?这是一个具有固定规格的玩具样品。不存在可维护性问题。我什至不会在这里费心任何花哨的事情,Linq 自动解决了这个问题:
但我同意,如果不习惯
Aggregate
函数,这可能会影响可读性。因此,更明确地说:其他一切都是过度设计。
For a fixed upper bound (100) I wouldn’t bother with this but ok …
But this
StringBuilder
isn’t only redundant, it’s really inefficient. And I don’t even need to benchmark to know this.And this just obscures the logic (this is supposed to implement the “fizzbuzz” problem, right?). Make the logic explicit.
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:
But I agree that this may strain readability if one isn’t used to the
Aggregate
function. So make it more explicit:Everything else is over-engineering.
假设您的代码只是您想要实现的目标的一个示例...创建更少 StringBuilder 的建议:
Assuming that your code is just an example of what you want to achieve... a proposal to create less StringBuilders:
如果我们让事情变得更困难一点怎么办?
1) 不允许进行除法或模运算;
2) 循环必须跳过所有不必要的迭代。
答案如下:
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: