听说有一个东西叫做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。C,Pascal,Python。
先解释一下这玩意怎么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代码。
最终的成品就如博客开头所示。
花了两个小时。。这玩意还是挺有趣的。
另外我今天下午也在浪浪浪,打广告