在最近的一次采访中,我被问到一个非常奇怪的问题 . 面试官问我如何使用编译器功能计算1 2 3 ... 1000 . 这意味着我不允许编写程序并执行它,但我应该编写一个程序,可以驱动编译器在编译时计算这个总和,并在编译完成时打印结果 . 作为提示,他告诉我,我可能会使用编译器的泛型和预处理器功能 . 可以使用C,C#或Java编译器 . 有任何想法吗???
这个问题与没有任何循环计算总和无关asked here . 另外,应该注意,总和应该在编译期间计算 . 使用C编译器指令仅打印结果是不可接受的 .
阅读有关已发布答案的更多信息,我发现使用C模板在编译期间解决问题称为 metaprogramming . 这是Erwin Unruh博士在标准化C语言过程中偶然发现的一种技术 . 您可以在wiki page of meta-programming上阅读有关此主题的更多信息 . 似乎可以使用Java注释在Java中编写程序 . 你可以看看下面的 maress's 答案 .
关于C中的元编程的一本好书是this one . 如果感兴趣,值得一看 .
一个有用的C元编程库是Boost的MPL this link .
13 回答
我觉得有义务提供这个C代码,因为还没有其他人:
而我需要做的就是检查装配以找到答案!
我明白了:
从Carl Walsh的答案扩展到在编译期间实际打印结果:
gcc输出:
您可以使用(并且主要是滥用)C宏/模板来执行metaprogramming . AFAIK,Java不允许同样的事情 .
Updated 现在改进了递归深度!适用于MSVC10和GCC而不会增加深度 . :)
简单的编译时递归添加:
Testcode:
GCC的输出:
Live example on Ideone .
MSVC10的输出:
编译时出错的C#示例 .
产生以下编译错误:
在编译期间打印数字的一个流行技巧是尝试访问使用您要打印的数字实例化的模板的不存在的成员:
然后编译器说:
有关此技术的更有趣示例,请参阅Solve the eight queens problem at compile-time .
由于在面试问题中既未指定编译器也未指定语言,我敢于使用GHC在Haskell中提交解决方案:
编译它:
我们也有一个工作计划 .
C 11增加了
constexpr
函数用于编译时计算,但是它们目前只支持gcc 4.6或更高版本 .该标准只要求编译器支持512的递归深度,因此它仍然需要避免线性递归深度 . 这是输出:
当然你可以使用公式:
在java中,我考虑过使用注释处理 . apt工具在将源文件实际解析为javac命令之前扫描源文件 .
在编译源文件期间,输出将被打印出来:
处理器工厂:
实际的注释处理器:
然后我们创建一个源文件 . 使用MyInterface注释的简单类:
注释处理器被编译成jar文件,然后apt工具用于编译源文件:
项目的输出:
这是一个在VC 2010下工作的实现 . 由于编译器在模板递归500次时抱怨,我不得不将计算分为3个阶段 .
编译时,您应该看到编译器的输出如下:
从理论上讲,你可以使用这个:
(根据Xeo发布的代码) . 但是GCC给了我这个错误:
加上一个巨大的伪堆栈跟踪 .
使用java,您可以对C#答案做类似的事情:
您可以在scala using peano numbers中执行此操作,因为您可以强制编译器执行递归,但我不认为您可以在c#/ java中执行相同的操作
另一个解决方案不是使用-Xprint,而是更加狡猾
不使用任何编译器标志 . 因为你可以检查任意数量的常数(不仅仅是500500),这个解决方案应该是可以接受的 .
虽然这实际上适用于小数字,但如果我使用sum_first,则clang会返回编译器错误,其中N> 400 .