UOJ Logo r_64的博客

博客

干了一件有趣的事情

2017-08-12 22:22:28 By r_64

听说有一个东西叫做Polyglot,就是写一份能由多种语言的编译器编译运行成功的程序。

然后我花了两个多小时摸索出了一个Pascal,C,Python3的Polyglot→_→(以UOJ上编译器为准)

(*c),d=2//len('''
;/*)var a,b:longint;begin read(a,b);writeln(a+b) end.*/
#include <stdio.h>
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}
/*'''),3
print(sum(map(int,input().split())))
#*/

见我的submission。CPascalPython

先解释一下这玩意怎么work的吧。首先考虑C语言:

(*c),d=2//len('''
;/*)var a,b:longint;begin read(a,b);writeln(a+b) end.*/
#include <stdio.h>
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}
/*'''),3
print(sum(map(int,input().split())))
#*/

C语言中是可以声明不带类型(默认int)的变量的,我试了一下(*c),d=2这种,发现照样可以。剩下的显然是个A+B,对吧?

然后考虑Pascal语言,长这样:

(*c),d=2//len('''
;/*)var a,b:longint;begin read(a,b);writeln(a+b) end.*/
#include <stdio.h>
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}
/*'''),3
print(sum(map(int,input().split())))
#*/

注意到pascal处理器会忽略end.后面的所有内容,这给我们写polyglot带来了一点小便利。然后再就是Pascal的注释有三种写法:(*comment*){comment}//comment

接下来看Python3:

(*c),d=2//len('''
;/*)var a,b:longint;begin read(a,b);writeln(a+b) end.*/
#include <stdio.h>
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}
/*'''),3
print(sum(map(int,input().split())))
#*/

这里用到了python3的一个语法:*操作符可以解包一个iterable object。例如a,*b,c=1,2,3,4,5会进行如下的赋值:a=1,b=(2,3,4),c=5。然后再注意到//是整除运算符,而且又是C里面的注释。

有了这个模板之后,就可以轻易地写出任意功能的C+Python3+Pascal的Polyglot啦!只要把A+B Problem的部分替换掉即可。

话说我不知道这个模板是不是还算短的。。毕竟Polyglot界人才辈出,我又只是个新手。。。

以及,我充分利用了这三个语言的语法特性,所以这个玩意是当不了C++和Python2的Polyglot的。如果谁构造出了C++和Python2的Polyglot请告知。(呃可能这些事情早已有人做过了吧,有的网站上甚至可以找到100个语言的Polyglot。。不过自己创造出一个Polyglot还是蛮爽的


下面是我的心路历程。。

首先,我在某地看到了一个C和Python3的Polyglot:

#include <stdio.h>/*
print(sum(map(int,input().split())))
'''}*/
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}//'''
#include <stdio.h>/*
print(sum(map(int,input().split())))
'''}*/
a,b;main(){scanf("%d%d",&a,&b);printf("%d",a+b);return 0;}//'''

这个程序厉害在哪呢?它的第一行是#include,以#开头,而这个符号是Python中的注释符。那么我们可以在第一行的行末放一个/*,然后第二行就归Python管了。Python将它的事情干完之后,又可以将控制权交给C。

(然而这个程序的idea并没有在我的成品中体现)

接下来要将Pascal加入这个Polyglot。简单起见,我们先考虑C与Pascal的Polyglot(扔掉Python)。这个时候麻烦就来了:第一行是#include吗?如果是的话Pascal编译器肯定GG,如果不是的话是什么呢?program a_plus_b或者var a,b:longint之类的东西,C++编译器就GG了。想要两个编译器都不GG,我们显然需要一个注释。然而又不能用两种语言共有的注释//。gcc看到文件的第一个字符是{的时候会报错,所以也不能用{comment}。fpc看到/*也会报错,那么最后的答案只有一个:(*

因为(*是Pascal的注释,所以接下来要考虑怎么伺候gcc。接下来显然是一个(*pts)一样的指针,又发现gcc中可以用(*pts)这样的语句定义变量,第一步就完成了:(*c)

接下来把控制权转交Pascal:(*c)/*)PAS,其中PAS是Pascal的A+B代码。

因为fpc在扫描到end.之后就退了,所以直接将控制权转给C即可。(*c)/*)PAS*/C,其中C是C的A+B代码。

(注:我Python3是今天现学的。。若下面理解有误请指出。。)

下一步就是加入Python啦。然而Pascal的到来使得#include不能作为开头了,我们需要另寻他法。幸运的是,Python3中也有以(*开头的语法:*后面接一个iterable(tuple/list之类的东西)表示将它“unpack”,例如print(*[2,3,4])相当于print(2,3,4)。但是这个语法不太好用。。因为必须以(*c开头(而不是(*[c),而*后面只能接iterable不能接int。不过我还是想到了(*c*[list])的方法,只不过(*c*[list])貌似在C中会CE。这很好办,我们用(*c//2*[list])即可。

现在的代码:(*c//2*[list])/*)PAS\n*/);C,然而Python方面还是过不了,原因是(*[list])不能被赋值,也不能参与加减乘除之类的运算,也不具有方法(总之就是不能写在表达式最左边)。我查了一会资料(事实上中间还走了若干弯路),发现有a,*b,c=1,2,3,4,5这种语法,最终调了一会才得出这样的第一行: (*c),d=2//len('''

这样有一个好处就是,第二行控制权给了C,在第二行开头写;/*)就可以把控制权交给Pascal,剩下的就很简单了:(*c),d=2//len('''\n;/*)PAS*/C/*'''),3,3;PY#*/即可,其中PY是Python3的A+B代码。

最终的成品就如博客开头所示。

花了两个小时。。这玩意还是挺有趣的。


另外我今天下午也在浪浪浪,打广告

评论

AnzheWang
66666,前排膜
laofu
有趣
vegchicken
orz
WrongAnswer
以前听说有给代码判断语言的题(?) 看来根据代码判断语言是不存在的事情。。。
mcfxmcfx
这个代码如果交到能自动识别语言的oj上会被识别成啥?。。
WuHongxun
666

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。