如何在编译时驱动C#、C ++或Java编译器计算1 + 2 + 3 + ... + 1000?

123
在最近一次面试中,我被问到一个非常奇怪的问题。面试官问我如何仅使用编译器功能计算1+2+3+...+1000。这意味着我不能编写程序并执行它,而是应该编写一个能够在编译时驱动编译器计算此总和并在编译完成时打印结果的程序。作为提示,他告诉我可以使用编译器的泛型和预处理器功能。可以使用C++、C#或Java编译器。有什么想法吗?
需要注意的是,这个问题与在没有任何循环的情况下计算总和无关,详情请参见此处。此外,必须在编译期间计算总和。仅使用C++编译器指令打印结果是不可接受的。
阅读了更多关于发布的答案的内容,我发现使用C++模板在编译期间解决问题被称为元编程。这是由Erwin Unruh 博士在标准化C++语言的过程中意外发现的技术。如果感兴趣,可以在元编程的wiki页面上了解更多关于此主题的信息。看来可以使用Java注释编写程序。你可以查看下面maress的答案。
一本关于C++元编程的好书是这本,如果感兴趣值得一看。
一个有用的C++元编程库是Boost的MPL,请点击此链接

17
编译错误算作“完成”吗?翻译完成。 - Mysticial
4
这个提示实际上是让你使用C++模板。显然不完全相同,但这个代码片段是用于打印1到1000的,我相信你可以修改它来添加到一千... https://dev59.com/9W455IYBdhLWcg3wD_oB - Joe
8
const int value = 1 + 2 + 3.... + 1000; Console.WriteLine(value);这段代码的意思是:定义一个整型常量value,其值为从1加到1000的结果,然后使用Console.WriteLine方法将value输出到控制台。 - George Duckett
9
有时候我觉得,有些面试问题仅仅是为了证明面试官在智力上比被面试者更加优越。 - Chris
4
在被问到这个问题之前,你有要求过很多钱吗? - Salman A
显示剩余13条评论
13个回答

1

使用Java,您可以做类似于C#答案的事情:

public class Cheat {
    public static final int x = (1000 *1001/2);
}

javac -Xprint Cheat.java

public class Cheat {

  public Cheat();
  public static final int x = 500500;
}

你可以使用Peano数在Scala中实现这个,因为你可以强制编译器进行递归,但我认为在C# / Java中无法做到同样的事情。

另一个解决方案是不使用-Xprint,但更加靠不住。

public class Cheat {
  public static final int x = 5/(1000 *1001/2 - 500500);
}

javac -Xlint:all Cheat.java

Cheat.java:2: warning: [divzero] division by zero
  public static final int x = 5/(1000 *1001/2 - 500500);
                            ^
1 warning

不使用任何编译器标志。由于您可以检查任意数量的常量(而不仅仅是500500),因此此解决方案应该是可接受的。

public class Cheat {
  public static final short max = (Short.MAX_VALUE - 500500) + 1001*1000/2;
  public static final short overflow = (Short.MAX_VALUE - 500500 + 1) + 1001*1000/2;

}

Cheat.java:3: error: possible loss of precision
  public static final short overflow = (Short.MAX_VALUE - 500500 + 1) + 1001*1000/2;
                                                                  ^
  required: short
  found:    int
1 error

很抱歉,您没有让编译器计算500500。 - Xeo
1
这是针对所有三个解决方案的参考吗?在解决方案1中,我拿了一些Java代码并编译它,编译器打印出500500。这看起来很像编译器计算了500500。那么这不是编译器计算500500吗? - benmmurphy
是的,确实如此,我在谈论解决方案2和3。我已经在早期的更新中阅读了这个答案,现在回到最新的更新上,不知怎么忘记了第一个解决方案。 - Xeo
我认为解决方案2和3也在计算它。你可以添加任意数量的检查,因此基本上是这样做的:for (i = 0; i < 100000; ++i) {if (i == 1000*1000/2) print i}。我有一个160MB的Java文件可以做到这一点,并且它可以正常工作 :) - benmmurphy

1
在理论上,你可以使用这个:
#include <iostream>

template<int N>
struct Triangle{
  static int getVal()
  {
    return N + Triangle<N-1>::getVal();
  }
};

template<>
struct Triangle<1>{
  static int getVal()
  {
    return 1;
  }
};

int main(){
   std::cout << Triangle<1000>::getVal() << std::endl;
   return 0;
}

(基于Xeo发布的代码)。但是GCC给我返回了这个错误信息:

triangle.c++:7: error: template instantiation depth exceeds maximum of 500 (use -ftemplate-depth-NN to increase the maximum) instantiating struct Triangle<500>

还有一个巨大的伪堆栈跟踪。


必须使用标志:-ftemplate-depth-1000 - Jetti
@hsalimi:是的。一旦添加了标志,它也适用于1000。但它不会在编译时打印,并且Xeo已经更改了他/她的答案以实际回答这个特定的问题,所以我认为我的答案已经过时了。 :-) - ruakh

1
尽管这对小数值有效,但当N > 400时,clang++会返回编译器错误,如果我使用sum_first。
#include <iostream>

using namespace std;


template <int N>
struct sum_first
{
   static const int value = N + sum_first<N - 1>::value;
};

template <>
struct sum_first<0>
{
    static const int value = 0;
};

int main()
{
    cout << sum_first<1000>::value << endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接