FizzBuzz 是一个非常简单的问题,因此它的简洁解决方案通常不可扩展且使用很少的抽象 - 在比较不同语言的解决方案时,这使得它们不太有趣。
FizzBuzzBazz 是 FizzBuzz 的一个变体,它添加了一个额外的规则 - 对于 7 的倍数,打印 Bazz
。因此,对于数字 35,人们会打印 BuzzBazz
,因为它既是 5 的倍数,也是 7 的倍数。这增加的复杂度恰到好处,使其不同的解决方案对于比较而言变得有趣。
我挑战你使用你选择的语言实现 FizzBuzzBazz,并找到你能想到的最短的正确程序。将你的解决方案发布在下面的评论中,或使用标签 #fizzbuzzbazz 发布推文 - 我将更新此页面以显示提交的最短程序。
当前获胜者:62 个字符。
FizzBuzzBazz 的规则如下
Fizz
。Buzz
。Bazz
。FizzBazz
,因为它既是 3 的倍数,也是 7 的倍数。我在 LiveScript 中的解决方案是 64 个字符(你能用你选择的语言击败它吗?)
[1 to 100]map ->[k+\zz for k,v of{Fi:3,Bu:5,Ba:7}|it%v<1]*''||it
该解决方案不是推荐的风格,但为了简洁起见,我删除了可选字符。
LiveScript 是一种编译成 JavaScript 的语言。它与 JavaScript 具有直接的映射关系,并允许你编写没有重复样板代码的表达性代码。虽然 LiveScript 添加了许多功能来帮助函数式编程,但它也对面向对象和命令式编程进行了许多改进。
现在,解释一下
我们必须根据特定规则集打印一个包含 100 个项目的列表。因此,我们的起点是从 1 到 100 的数字列表。为此,我们将使用范围文字
[1 to 100]
我们希望通过对列表的每个成员应用相同的规则来创建一个相同数量项目的新的列表。为此,我们可以使用 map
函数,它是较新版本的 JavaScript(以及 LiveScript)中所有数组的属性。
[1 to 100].map(...)
map
将一个函数作为其参数,它将其应用于输入列表的每个成员。map
获取的函数必须具有单个参数,并返回一个值,该值将作为输出列表中的对应值。在 LiveScript 中,函数由指向函数体的箭头定义。
[1 to 100].map((x) -> ...)
但是我们如何确定为我们的 map 函数的每个输入返回什么?
规则规定,对于 3 的倍数,我们打印 Fizz,对于 5 的倍数,我们打印 Buzz,对于 7 的倍数,我们打印 Bazz。我们可以用对象文字表示这种关系
{Fizz: 3, Buzz: 5, Bazz: 7}
我们需要将这组规则转换为输出。由于可能存在一个数字是多个数字的倍数,因此可能有多个规则通过。因此,我们必须过滤对象并生成结果列表。当没有规则通过时,空列表将是这种情况。
为此,我们可以在对象上使用列表推导。使用 of
为我们提供了对象中每个项目的键和值。我们希望通过对值(例如 3)应用特定规则集进行过滤来输出键(例如 Fizz
)。格式为 [output for values of input when condition]
[key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when ...]
条件非常简单,如果除法没有余数,则一个数字是另一个数字的倍数。(记住 x
是我们正在映射的值。)
[key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when x % value == 0]
这会生成一个列表,但我们不需要列表,我们需要一个字符串。因此,我们连接列表。
[key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when x % value == 0].join('')
将我们到目前为止的内容放在一起 - 请注意,LiveScript 隐式地返回函数体中的最后一个表达式(除非另有指定),因此不需要 return
语句。
[1 to 100].map((x) -> [key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when x % value == 0].join('') )
但是,上述内容并不能给我们想要的结果 - 当没有规则通过时,我们不是打印空字符串,而是希望打印输入的数字。在 JavaScript 中,空字符串是“假值”。因此,我们可以使用 or
运算符 - 当左侧为真值时,它计算为左侧,当左侧为假值时,它计算为右侧。
[1 to 100].map((x) -> [key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when x % value == 0].join('') or x )
它有效!上述内容计算为具有正确结果的列表。但是,它有点长,109 个字符,因此我们可以稍微缩短一下。
首先,在大多数情况下,调用函数时不需要括号。
[1 to 100].map (x) -> [key for key, value of {Fizz: 3, Buzz: 5, Bazz: 7} when x % value == 0].join('') or x
我们还可以缩短我们使用的变量名
[1 to 100].map (x) -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} when x % v == 0].join('') or x
当函数只有一个参数时,我们可以使用 it
来隐式地引用该参数,而无需显式地定义它。因此,在这种情况下,it
替换了 x
。
[1 to 100].map -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} when it % v == 0].join('') or it
如果乘法运算符 *
的右侧是字符串文字,则它会将左侧的列表连接起来。因此,我们可以替换 .join('')
。
[1 to 100].map -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} when it % v == 0] * '' or it
|
是 when
的别名。
[1 to 100].map -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} | it % v == 0] * '' or it
出于我们的目的,我们不需要担心负数,并且我们只处理整数,因此 < 1
等价于 == 0
。
[1 to 100].map -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} | it % v < 1] * '' or it
我们做得不错,但我们可以做得更好。在明确的情况下,点是可选的,因此我们可以缩短 map
属性访问。
[1 to 100]map -> [k for k, v of {Fizz: 3, Buzz: 5, Bazz: 7} | it % v < 1] * '' or it
提取我们在所有规则中找到的 zz
,使用 \word
形式的单词文字,留下
[1 to 100]map -> [k + \zz for k, v of {Fi: 3, Bu: 5, Ba: 7} | it % v < 1] * '' or it
最后,我们可以删除一些空格。因为 or
需要在其周围留出空格,所以我们可以用 ||
替换它,||
不需要空格。
[1 to 100]map ->[k+\zz for k,v of{Fi:3,Bu:5,Ba:7}|it%v<1]*''||it
我们代码高尔夫努力的最终结果:总共 64 个字符。
请注意,我们的解决方案易于扩展,因为我们的规则被声明为数据而不是命令式代码。
感谢 Ian Barfield 在 LiveScript 中创建了一个更短的解决方案!
[++x>?[k+\zz for k,v of{Fi:3,Bu:5,Ba:7}|x%v<1]*'' for x to 99]
到目前为止,62 个字符的 LiveScript 解决方案是最短的 FizzBuzzBazz 程序。你认为你能用你选择的语言击败它吗?在下面的评论中输入你的尝试,或使用标签 #fizzbuzzbazz 发布推文。如果找到更短的程序,我将更新此部分以显示新的结果。有关 LiveScript 的更多信息,请查看其 官方网站。如果你对函数式编程感兴趣,你可能想查看 使用 LiveScript 和 prelude.ls 进行 JavaScript 函数式编程。
有关 LiveScript 和 prelude.ls 的更多信息,关注 @gkzahariev。
评论由 Disqus 提供支持