游戏规则
原文地址
https://github.com/eatnumber1/goal/blob/master/README.md
翻译
以下翻译复制于FreeBuf
- 规则就是鼓励你打破规则,要的就是你机智!
- 程序执行时,该方案要能打印出带足够多的’o’的”goal”来展示程序的功能;
- 代码g()()('al')必须出现在源代码中。
- g()('al')不能是一个字符串文字;
- 而'al'必须是字符串,或是其他语言中的等价类型,你可以用创建自己的一套语言标准方法(例如C语言必须用",Ruby可以用"或'的任意一种;
- g()('al')在你语言中必须是有效的右值(如果适用的话)
- g()('al')也许不能输出这样一个字符串,如果真的在你的语言中不能输出一个字符串,你应该提交一个原理解释 :为什么它不能一个打印出一个可接受字符串的方案呢!
- 你必须能够插入任意数量的()以调用,而不用修改你的方案。如果非要修改原方案那么它就是失败的。
- 噢,提醒一下g('al')必须返回”gal”。
PS: 看到这里,如果想自己小试身手可以先不往下看,自己先写一段可以运行的代码,再继续往下
分析
这个规则看起来有点复杂,让先理一理规则,思考一下这个游戏到底要我们做什么。
由于游戏要求用不同的语言实现,支持()调用在不同的语言里可能是不同的东西,例如:C++可能是仿函数,可能是构造函数等。以下都用函数指代可以执行()调用的实体。
- 写一个名为g的函数
- g函数要有2个重载
- 有1个参数时,返回 g+参数
- 没有参数时,返回一个函数,我叫它o函数,o函数也需要2个重载
- 有1个参数时,返回 g+o+参数
- 没有参数时,再返回一个类似o的函数,区别是它在带1个参数时,要加2个o
- …以此类推
PS: 看到这里,如果还没有自己小试身手的想法,建议就不要浪费时间再往下看了,看了也白看
解决方法
由于最近初学lua,lua的函数是first-class的,所以我的直觉就是lua能非常简单地解决这个问题,所以下面全是lua代码的实现。其他任何函数是first-class或者语言级支持闭包的语言,都能很容易移植或者改写。
第一次尝试
问题有点复杂,让我们把问题分为两个部分
- 约束的规则
- 各种古怪的调用方式
- 期望的结果
- 生成"g"+"o"*n+"al",例如:gal, goal, gooal等
这样看来,第1个部分太难,我们尝试放着第1部分不管,只实现第2部分的要求
1 2 3 4 5 6 7 |
local n = 3 local result = 'g' for i=1,n do result = result .. 'o' end result = result .. 'al' print(result) |
结果跟预期的一样,现在只需要修改n的值,就能得到n个o的goal。
第二次尝试
看来进展挺顺利,让我们继续往下,基于第1部分的规则,我们要写一个g函数,支持2种重载,返回不同的值(字符串/o函数),返回的o函数也是类似的,支持2种重载,返回不同的值(字符串/函数)。所以,我考虑o能不能就是g呢?先假设这是可行的,让我们来试试
现在分析一下2种情况的重载
- 有1个参数时,返回 'g'+'o'*(调用g的次数-1)+参数
- 没有参数时,返回g自己
1 2 3 4 5 6 7 8 9 |
local o = '' local function g(str) if str then return 'g'..o..str end o = o .. 'o' return g end print(g()()()()('al')) |
可以看到,我们用一个闭包,很容易地解决了这个问题,当每次g被无参数调用的时候,o就加上一个'o'。
第三次尝试
上面的代码看起来好像很完美了,但是g()()()()('al')不能重复调用,因为g共享了同一个o变量。但其实这很容易解决。
1 2 3 4 5 6 7 8 9 10 11 12 |
local o = '' local function g(str) if str then local oo = o o = '' return 'g'..oo..str end o = o .. 'o' return g end print(g()()()()('al')) print(g()()('al')) |
是的,在最后返回之前,重置一下o就行了。这就是传说中的“递归结束条件”。
第4次尝试
如果上面的代码看起来已经完全符合游戏规则,甚至符合了我们自己增加的条件(因为规则并没有要求,能够重复调用)。既然已经加了个规则,我们最后尝试一下,再加个规则,就是当g没有调用g('al')时,例如:g()(),后面再调用g()()('al'),也应该可以正确输出gooal。
为了符合这个条件,我们只能让g返回另一个函数,然后实现一个真正的闭包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
local function g(str) local o = '' local function fo(str) if str then return 'g'..o..str end o = o .. 'o' return fo end return fo(str) end g()() print(g()()()()('al')) print(g('al')) print(g()()('al')) |
至此,我觉得已经玩够了,该结束了。不知道有没有把问题描述清楚,我想说的是,再复杂的问题,只要我们理解清楚需求,拆分成小模块去逐一击破。到最后甚至可以发现规则的漏洞,自己去加强规则,玩转规则。