放松一下,来个编程挑战(不限于C/C++,而且答对有奖励)
咱们论坛的帖子多以问/答为主,今儿来点脑筋急转弯吧。这个问题很早在CSDN/C版上看到过:
要求实现这样的函数:
int p(int i, int N);
功能:调用该函数,打印如下格式的输出,例p(1, 7);
1
2
3
4
5
6
7
6
5
4
3
2
1
即每行一个数字。(注意:N只打印一次)
要求:
1. 函数中唯一能够调用的函数就是printf。(是不是意味着不能递归?)
2. 不准使用如下的关键字:typedef, enum, do, while, for, switch, case, break, continue, goto, if。 (通常的循环也别想了?)
3. 不能使用逗号表达式和?:表达式。
4. 函数中只能有一条语句(就是只能出现一个;)。
PS:如果你使用其他语言,也请尽量满足对应语言中与上述4条相对应的规则,并作出相应的说明!
怎么样,各位,贴出你的解决方案吧,我私自做主了,一个转贴的答案+5点体能,一个原创的答案+1点威望。
=================================解答分割线===========================
第一个满足全部约束条件的代码如下,由wqsong 提供,使用C语言巧妙地实现,详细分析见10L、15L、17L等:
int p(int i, int N)
{
return (*((short *)&i+1) || (*((short *)&i+1) = *(short *)&i)) && *(short *)&i <= (2*N - (*((short *)&i+1))) &&printf("%d\n", N - (N - *(short *)&i) * (2*(*(short *)&i < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*(short *)&i;
}
第二个?It's up to you!
。。。神马奖励??? 应该只能用递归函数的,不用递归函数只能内嵌汇编了。。。;@P 本帖最后由 wqsong 于 2010-10-18 10:09 编辑
int p(int i, int N)
{
return i<N && printf("%d\n", i) && !p(i+1, N) || printf("%d\n", i);
}直接看有点绕,分解一下,有点类似与二叉树的后序遍历:
int p(int i, int N)
{
if(i<N)
{
printf("%d\n", i);
p(i+1, N);
}
printf("%d\n", i);
}以上这段不难懂,从上往下分别用到i<N,printf("%d\n", i),p(i+1, N),printf("%d\n", i)。。。
返回一个bool值。。。再用&&和||的短路特性连接一下就可以了。。。 python的一个,python 2.5.4的print不带返回值,3.0以后好像带返回值,改写一下,有点多。。。def printf(i):
print i
return idef p(i, N):
return i<N and printf(i) and not p(i+1, N) or printf(i) 本帖最后由 wqsong 于 2010-10-18 10:07 编辑
int p(int i, int N)
{
return (i<N) * printf("%d\n", i) && p(i+1, N) && 0 || printf("%d\n", i);
}这样也可以。。。有点fuck brain。。。 本帖最后由 wqsong 于 2010-10-18 10:33 编辑
int p(int i, int N)
{
printf("%d\n", i) && i<N && p(i+1, N) && printf("%d\n", i);
}再发一个,return语句都省了。。。没返回值和函数原型有点不一样,但是符合C89标准。
回复 wqsong 的帖子
不错不错,不过原问题中有一个限制没有满足:
1. 函数中唯一能够调用的函数就是printf。
是不是意味着甚至不能调用P()本身?
不过谢谢你积极的参与和非常有创意的工作,分加上了,有空常来逛逛。
回复 Rainyboy 的帖子
。。。应该不限制递归吧
再想想看有么有不用递归的办法。。。 本帖最后由 wqsong 于 2010-10-18 14:50 编辑
想了一会,估计不用递归的话,修改IP寄存器也能做到。。。有点繁琐,而且调用也有限制。。。
假设有这样的调用:
p(int i, int N);
对应的汇编应该是:
……
push N
push i
call p
……
因为call指令要执行的是push eip(这里不考虑段间调用),所以实际上在栈中存储是这样的
+--------+
| … | <-这里是低地址
+--------+
| eip |
+--------+
| i |
+--------+
| N |
+--------+
| … | <-这里是高地址
+--------+
所以IP实际的压栈位置应该是((unsigned *)&i - 1)(按32位机器算,一个指针占4字节),每次把这位置的值修改到call foo处,就能达到目的。
原理是这样的,估计实现的时候还有其他问题,当然和编译器也有关。。。
当然,同理,如果知道printf()函数具体怎么实现的话,直接修改进入printf函数时候的IP指向也可以。。。
就不写了,娱乐一下拉到。。。:lol
回复 wqsong 的帖子
好吧……确实很程序员方式的娱乐,有空我还真想试试你所说的方法,估计等两天吧 回复 Rainyboy 的帖子
嗯嗯,有空我也试一试。。。
快毕业了,忙的找工作,什么心情也没有。。。嘿嘿 本帖最后由 wqsong 于 2010-10-19 01:22 编辑
int p(int i, int N)
{
return (*((short *)&i+1) || (*((short *)&i+1) = *(short *)&i)) && *(short *)&i < 2*N && *(short *)&i <= (2*N - (*((short *)&i+1))) &&printf("%d\n", N - (N - *(short *)&i) * (2*(*(short *)&i < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*(short *)&i;
}再写一个,当作我的最终版吧。。。
只用到了printf函数,没有显式的递归。隐式修改eip实现递归。同时把i变量分为两半用,是一个16位过程。当然和编译器实现有关,在VC9,GCC3.4.5均能实现。
回复 wqsong 的帖子
真是有朋自远方来不亦说乎啊!欢迎你有空常来这边逛逛 本帖最后由 Rainyboy 于 2010-10-20 10:59 编辑
我来慢慢重构,理解你的代码:
源代码:
int p(int i, int N)
{
return (*((short *)&i+1) || (*((short *)&i+1) = *(short *)&i)) && *(short *)&i < 2*N && *(short *)&i <= (2*N - (*((short *)&i+1))) &&printf("%d\n", N - (N - *(short *)&i) * (2*(*(short *)&i < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*(short *)&i;
}第一步:
int p(int i, int N)
{
short *funcp = (short *)&i;
return (*(funcp+1) || (*(funcp+1) = *funcp)) && *funcp < 2*N && *funcp <= (2*N - (*(funcp+1))) &&printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*funcp;
}
第二步:
int p(int i, int N)
{
short *funcp = (short *)&i;
if((*(funcp+1) || (*(funcp+1) = *funcp)))
{
return *funcp < 2*N && *funcp <= (2*N - (*(funcp+1))) &&printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*funcp;
}
else
{
return 1;
}
}第三步:
int p(int i, int N)
{
short *funcp = (short *)&i;
if((*(funcp+1) > 0))
{
return *funcp < 2*N && *funcp <= (2*N - (*(funcp+1))) &&printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*funcp;
}
else
{
*(funcp+1) = *funcp;
if(*(funcp+1) > 0)
{
return *funcp < 2*N && *funcp <= (2*N - (*(funcp+1))) &&printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*funcp;
}
else
{
return 1;
}
}
}
第四步:
int p(int i, int N)
{
short *funcp = (short *)&i;
//i的低位存于funcp中,用于计数
//i的高位存于funcp+1中,用于储存i的实际值
if((*(funcp+1) > 0))//该分支用于判断是否初始化
{
//if(*funcp < 2*N)//该分支用于判断是否到达打印次数:多余的判断!
//{
if(*funcp <= (2*N - (*(funcp+1))))//该分支用于判断是否到达打印次数
{
printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1));//执行打印
*((unsigned *)&i - 1) -= 5;//调整堆栈中eip的值
++*funcp;//计数器自增
}
return 1;
//}
//return 1;
}
else
{
*(funcp+1) = *funcp;
if(*(funcp+1) > 0)
{
//if(*funcp < 2*N)//该分支用于判断是否到达打印次数:多余的判断!
//{
if(*funcp <= (2*N - (*(funcp+1))))//该分支用于判断是否到达打印次数
{
printf("%d\n", N - (N - *funcp) * (2*(*funcp < N) - 1));//执行打印
*((unsigned *)&i - 1) -= 5;//调整堆栈中eip的值
++*funcp;//计数器自增
}
return 1;
//}
//return 1;
}
return 1;
}
}
第五步:
通过上述重构发现你的代码中似乎存在一条多余的判断,见上述注释。
因此,将这条判断减去,并且重新合并后,修正一个版本:
int p(int i, int N)
{
return (*((short *)&i+1) || (*((short *)&i+1) = *(short *)&i)) && *(short *)&i <= (2*N - (*((short *)&i+1))) &&printf("%d\n", N - (N - *(short *)&i) * (2*(*(short *)&i < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*(short *)&i;
}即:
int p(int i, int N)
{
return (*((short *)&i+1) || (*((short *)&i+1) = *(short *)&i)) /*&& *(short *)&i < 2*N*/ && *(short *)&i <= (2*N - (*((short *)&i+1))) &&printf("%d\n", N - (N - *(short *)&i) * (2*(*(short *)&i < N) - 1)) && (*((unsigned *)&i - 1) -= 5) && ++*(short *)&i;
}
页:
[1]
2