锡兰,“没有I / O的紫色”,662
import ceylon.language{l=variable,I=Integer,m=map,S=String}class M(S d){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v)};I&I(I)x{throw Exception(d);}I h(I v)=>f[v]?.first else x;shared void p(){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}shared void run(){value a='!'..'~';{S*}s=expand(loop<{S*}>{""}((g)=>{for(c in a)for(p in g)p+"``c``"}));l{M*}r={};for(p in s){r=[M(p),*r];for(e in r){try{e.p();}catch(x){print(x.message);r=r.filter(not(e.equals));}}}}
紫色是一种自我修饰的单指令语言,要求在这里进行解释。作为输入和输出是不相关的这个任务,我删除了o
从解释符号的意义,使得(潜在的)有效符号只是a
,b
,A
,B
,i
和1
(最后一个只是为了读书,不是为了写)。
但是,由于Purple正在自我修改(并使用其源代码作为数据),可能包含其他字符的程序也很有用,因此我选择允许代码中所有可打印的(非空白)ASCII字符(其他字符可能是也很有用,但不那么容易打印)。
(您可以修改解释器,以取而代之以允许的字符串作为命令行参数–切换a
下面定义的注释行。然后长度变为686字节。)
因此,我的“并行”解释器从这些字符创建了所有有限的字符串(以递增的长度和字典顺序)并尝试了每个有限的字符串。
每当从磁带上读取要执行的命令无效时,Purple都会无错误地停止运行-因此,没有无效的程序,并且有许多停止的程序。(即使在第一步中大多数都停止,只有一些长度为3的程序才能进入第二步(然后将停止),第一个非停止的程序的长度为6。
我认为按我的解释器尝试的顺序,第一个非停止程序是aaaiaa
,它在第一步中将a
寄存器设置为0(已经是),在第二步以及随后的每个步骤中,将指令指针设置为0,使它iaa
再次执行。
我重用了为“标准” Purple解释器编写的部分代码,但是由于删除了输入和输出,因此并行解释器的长度甚至比该解释器更短,同时还包含了一次执行多个程序的附加逻辑。
这是带注释的格式版本:
// Find (enumerate) all halting programs in (a non-I/O subset of) Purple.
//
// Question: /codegolf//q/51273/2338
// My answer: /codegolf//a/65820/2338
// We use a turing-complete subset of the Purple language,
// with input and output (i.e. the `o` command) removed.
import ceylon.language {
l=variable,
I=Integer,
m=map,
S=String
}
// an interpreting machine.
class M(S d) {
// The memory tape, as a Map<Integer, Integer>.
// We can't modify the map itself, but we
// can replace it by a new map when update is needed.
l value t = m {
// It is initialized with the code converted to Integers.
// We use `.hash` instead of `.integer` because it is shorter.
*d*.hash.indexed
};
// three registers
l I a = 0;
l I b = 0;
l I i = 0;
// get value from memory
I g(I j) =>
t[j] else 0;
// Map of "functions" for fetching values.
// We wrap the values in iterable constructors for lazy evaluation
// – this is shorter than using (() => ...).
// The keys are the (Unicode/ASCII) code points of the mapped
// source code characters.
value f = m {
// a
97 -> { a },
// b
98 -> { b },
// A
65 -> { g(a) },
// B
66 -> { g(b) },
// i
105 -> { i },
// 1
49 -> { 1 }
};
// Map of functions for "storing" results.
// The values are void functions taking an Integer,
// the keys are the ASCII/Unicode code points of the corresponding
// source code characters.
value s = m {
// a
97 -> ((I v) => a = v),
// b
98 -> ((I v) => b = v),
// Modification of the memory works by replacing the map with
// a new one.
// This is certainly not runtime-efficient, but shorter than
// importing ceylon.collections.HashMap.
// A
65 -> ((I v) => t = m { a->v, *t }),
// B
66 -> ((I v) => t = m { b->v, *t }),
// i
105 -> ((I v) => i = v)
};
// Exit the interpretation, throwing an exception with the machine's
// source code as the message. The return type is effectively `Nothing`,
// but shorter (and fits the usages).
I&I(I) x {
throw Exception(d);
}
// accessor function for the f map
I h(I v) =>
f[v]?.first else x;
// a single step
shared void p() {
(s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
i += 3;
}
}
// the main entry point
shared void run() {
// the alphabet of "Purple without I/O".
value a = '!'..'~';
//// possible alternative to use a command line argument:
// value a = process.arguments[0] else '!'..'~';
// an iterable consisting of all programs in length + lexicographic order
{S*} s =
// `expand` creates a single iterable (of strings, in this case)
// from an iterable of iterables (of strings).
expand(
// `loop` creates an iterable by applying the given function
// on the previous item, repeatedly.
// So here we start with the iterable of length-zero strings,
// and in each iteration create an iterable of length `n+1` strings
// by concatenating the length `n` strings with the alphabet members.
loop<{S*}>{ "" }((g) =>
{
for (c in a)
for (p in g)
p + "``c``"
}));
// This is a (variable) iterable of currently running machines.
// Initially empty.
l {M*} r = {};
// iterate over all programs ...
for(p in s) {
// Create a machine with program `p`, include it
// in the list of running machines.
//
// We use a sequence constructor here instead of
// an iterable one (i.e. `r = {M(p, *r)}` to prevent
// a stack overflow when accessing the deeply nested
// lazy iterable.
r = [M(p), *r];
// iterate over all running machines ...
for(e in r) {
try {
// run a step in machine e.
e.p();
} catch(x) {
// exception means the machine halted.
// print the program
print(x.message);
// remove the machine from the list for further execution
r = r.filter(not(e.equals));
}
}
// print(r.last);
}
}
"If your program outputs itself, it is probably wrong or a polyglot."