编写一些代码,以打印出以下连续数字范围:
数字“ fizz”表示3的倍数
'buzz'表示5的倍数的数字
'fizzbuzz'表示15的倍数的数字
例如如果我在1到20的范围内运行程序,我应该得到以下输出
1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz
这种尝试是通过这种方式完成的,因为我需要在控制台上打印序列,并且我需要以显示相应的测试,例如TDD。
public static class Evaluate
{
public static string FizzBuzz(int start, int end)
{
return Enumerable.Range(start, (end - start) + 1)
.Select(FizzOrBuzz)
.Aggregate(String.Empty, (y, x) => String.Format("{0} {1}", y, x))
.Trim();
}
public static string FizzOrBuzz(int n)
{
if (n % 15 == 0) return "fizzbuzz";
if (n % 3 == 0) return "fizz";
if (n % 5 == 0) return "buzz";
return n.ToString();
}
}
#1 楼
我宁愿使用String.Join
方法代替Aggregate
:public static string FizzBuzz(int start, int end)
{
return String.Join(" ", Enumerable.Range(start, end - start + 1).Select(FizzOrBuzz));
}
这应消除多个字符串连接。
编辑。删除了多余的泛型类型参数。
评论
\ $ \ begingroup \ $
请注意,泛型类型参数是多余的,可以从FizzOrBuzz的签名中推断出它们-因此.Select(FizzOrBuzz)同样适用。
\ $ \ endgroup \ $
– Mathieu Guindon♦
15年1月13日在17:31
\ $ \ begingroup \ $
@ Mat'sMug我已经删除了通用类型参数。谢谢。
\ $ \ endgroup \ $
–德米特里
15年1月13日在19:28
#2 楼
你的名字不是很好。例如,作为类名的Evaluate
并不能告诉我任何信息。从TDD的角度来看,最好使用生成的序列,而不仅仅是生成的字符串。这样就可以测试预期的元素数量之类的东西。
方法的实际逻辑是好的。
使用其他方法的重命名版本:
public static class FizzBuzz
{
public static string GenerateDisplayString(int start, int end)
{
return GenerateSequence(start, end)
.Aggregate(String.Empty, (y, x) => String.Format("{0} {1}", y, x))
.Trim();
}
public static IEnumerable<string> GenerateSequence(int start, int end)
{
return Enumerable.Range(start, (end - start) + 1)
.Select(GetForDisplay);
}
private static string GetForDisplay(int number)
{
if (number % 15 == 0) return "fizzbuzz";
if (number % 3 == 0) return "fizz";
if (number % 5 == 0) return "buzz";
return number.ToString();
}
}
然后我将测试
GenerateSequence
方法。评论
\ $ \ begingroup \ $
命名困扰了我。这是一个定时锻炼,所以我没有过多地讲。我同意将显示关注点提取到单独的功能中。感谢您的时间。
\ $ \ endgroup \ $
–chosenbreed37
15年1月13日在13:12
#3 楼
您确定您的要求吗?特别是这样:'fizzbuzz'表示数字为15的倍数
问题通常表示为
如果数字是3和5的倍数,请打印“ FizzBuzz”。
如果面试官给了您发布的要求,那么您就很好。但是,如果需求实际上是后者,那么您就错过了保持代码灵活和DRY的机会。
根据经典要求,假设数字已更改,您现在需要以5的倍数打印“ Fizz”,以7的倍数打印“ Buzz”。除了对代码进行两次更改外,您将必须制作三个。不仅如此,维护人员还需要了解15是3和5的最小公倍数,以便能够计算“ FizzBuzz”数字的新常数。您可能最好删除15个常数,然后将其设为真正的常数:其他两个常数的最小公倍数。
评论
\ $ \ begingroup \ $
我从字面上复制并粘贴了需求(脱机测试)。我同意你的观点。
\ $ \ endgroup \ $
–chosenbreed37
15年1月13日在13:01
\ $ \ begingroup \ $
如果FizzBuzz中有任何神圣的战争,就是这样。
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年1月13日在13:03
\ $ \ begingroup \ $
“经典要求”是对受访者能否完全编程的考验。这对它的考虑太多了。
\ $ \ endgroup \ $
– RemcoGerlich
15年1月13日在14:55
\ $ \ begingroup \ $
@RemcoGerlich,您可能会觉得很幽默。
\ $ \ endgroup \ $
–RubberDuck
15年1月13日在14:57
\ $ \ begingroup \ $
这似乎是假设给您的要求以最能描述其基础结构的方式陈述了问题。在现实生活中通常并非如此,发现结构是开发人员工作的一部分。
\ $ \ endgroup \ $
–本·亚伦森
15年1月14日在16:45
#4 楼
我建议对序列中的每个项目使用模数或除法运算符不必要地昂贵,与简单的加法和比较相比,两者都占用大量处理器。避免重复使用%
。我提供了一个可选的start
参数,因此该系列可以在传统开始之后启动,而不会浪费Skip
。 public static IEnumerable<string> FizzBuzzSeries(long start = 1)
{
const ulong FFizz = 3UL;
const ulong FBuzz = 5UL;
if (start < 1)
{
throw new ArgumentOutOfRangeException("start");
}
var i = (ulong)start;
var fizz = FFizz;
if (i > FFizz)
{
fizz = i + (FFizz - (i % FFizz));
}
var buzz = FBuzz;
if (i > FBuzz)
{
buzz = i + (FBuzz - (i % FBuzz));
}
var s = new StringBuilder(8);
for (; i <= ulong.MaxValue; i++)
{
if (i == fizz)
{
fizz += FFizz;
s.Append("fizz");
}
if (i == buzz)
{
buzz += FBuzz;
s.Append("buzz");
}
if (s.Length > 0)
{
yield return s.ToString();
s.Clear();
}
else
{
yield return i.ToString(CultureInfo.InvariantCulture);
}
}
}
要获得所需的输出,
Console.WriteLine(string.Join(" ", FizzBuzzSeries().Take(20)));
评论
\ $ \ begingroup \ $
好的,我想这可行,但是我认为使用else比使用continue更好。功能继续非常接近goto声明,而IMO也有类似的问题。否则会很好。
\ $ \ endgroup \ $
– Hogan
15年1月14日在16:58
\ $ \ begingroup \ $
@Hogan,随您便。
\ $ \ endgroup \ $
–乔德雷尔
15年1月14日在16:59
\ $ \ begingroup \ $
@Jodrell一种新颖的(至少对我来说)实现。做得很好。我喜欢这个解决方案。也许下次我在面试时会用到它:-)
\ $ \ endgroup \ $
–chosenbreed37
15年1月15日在17:10
\ $ \ begingroup \ $
@Jodrell ...说过,如果执行此操作,所有效率似乎都将丢失:FizzBuzzSeries.Skip(1000000).Take(20)...
\ $ \ endgroup \ $
–chosenbreed37
15年1月15日在17:52
\ $ \ begingroup \ $
@ chosenbreed37我添加了一个可选的开始参数。
\ $ \ endgroup \ $
–乔德雷尔
15年1月16日在9:53
#5 楼
简单的事情要保持简单。我认为一个问题应该在一行中解决:return String.Join(
Environment.NewLine,
Enumerable.Range(start, (end - start) + 1)
.Select(n => n % 15 == 0 ? "fizzbuzz"
: n % 3 == 0 ? "fizz"
: n % 5 == 0 ? "buzz"
: n.ToString())
评论
\ $ \ begingroup \ $
为什么您认为这比投票最多的答案更简单?我不认为这要简单得多。
\ $ \ endgroup \ $
–递归
15年1月13日在18:48
\ $ \ begingroup \ $
就编译成什么而言简单?可能不会。但是,它与顶部答案不同,因为该方法是内联的,因此您可以在那里直接阅读它,并且方法主体为一行而不是4行,因此您无需在lambda内使用方括号和分号。
\ $ \ endgroup \ $
–挡风板
2015年1月13日在23:47
\ $ \ begingroup \ $
我不是在谈论MSIL编译。我在说代码。肯定是更少的语句,但是我认为这并不容易阅读。陈述并不总是不好的。例如,它们确实使添加断点变得非常容易。
\ $ \ endgroup \ $
–递归
15年1月14日在1:29
\ $ \ begingroup \ $
@recursive-我认为代码本身就是正确的,试图解释为什么我认为它更简单是没有意义的。每个人都会发现它是否简单。我并不是说它更好,因为它更简单-这是另一个问题。但我确实认为,在这种情况下,行数减少一半,对象名称减少5个。在其他情况下,减少名称的行数和数量会使情况变得更复杂...但是我认为在这种情况下不是这样。
\ $ \ endgroup \ $
– Hogan
15年1月14日在16:50
\ $ \ begingroup \ $
很公平。
\ $ \ endgroup \ $
–递归
15年1月14日在23:45
评论
您可以将FizzOrBuzz设为私有,除此之外,一切对我来说似乎都很好@RubberDuck显然您还没有看到此代码:codereview.stackexchange.com/questions/49058/…
@SimonAndréForsberg好点。讨论促使我发布了问题。
奇怪地类似于这个答案stackoverflow.com/a/5661471/659190
我永远不会理解对模块运算符进行嗡嗡嗡嗡的不健康困扰。专业职业实践:从不。采访筛查的发生:始终。