Rainyboy 发表于 2010-10-16 10:17

放松一下,来个编程挑战(不限于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!

wqsong 发表于 2010-10-18 00:40

。。。神马奖励???

wqsong 发表于 2010-10-18 00:42

应该只能用递归函数的,不用递归函数只能内嵌汇编了。。。;@P

wqsong 发表于 2010-10-18 00:50

本帖最后由 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值。。。再用&&和||的短路特性连接一下就可以了。。。

wqsong 发表于 2010-10-18 01:36

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:05

本帖最后由 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:24

本帖最后由 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标准。

Rainyboy 发表于 2010-10-18 10:29

回复 wqsong 的帖子

不错不错,不过原问题中有一个限制没有满足:
1. 函数中唯一能够调用的函数就是printf。

是不是意味着甚至不能调用P()本身?

不过谢谢你积极的参与和非常有创意的工作,分加上了,有空常来逛逛。

wqsong 发表于 2010-10-18 10:38

回复 Rainyboy 的帖子

。。。应该不限制递归吧
再想想看有么有不用递归的办法。。。

wqsong 发表于 2010-10-18 13:49

本帖最后由 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

Rainyboy 发表于 2010-10-18 13:53

回复 wqsong 的帖子

好吧……确实很程序员方式的娱乐,有空我还真想试试你所说的方法,估计等两天吧

wqsong 发表于 2010-10-18 14:01

回复 Rainyboy 的帖子

嗯嗯,有空我也试一试。。。
快毕业了,忙的找工作,什么心情也没有。。。嘿嘿

wqsong 发表于 2010-10-18 23:19

本帖最后由 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均能实现。

Rainyboy 发表于 2010-10-19 22:16

回复 wqsong 的帖子

真是有朋自远方来不亦说乎啊!欢迎你有空常来这边逛逛

Rainyboy 发表于 2010-10-20 10:34

本帖最后由 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
查看完整版本: 放松一下,来个编程挑战(不限于C/C++,而且答对有奖励)