代码之家  ›  专栏  ›  技术社区  ›  Frank Perez

图灵机器代码高尔夫

  •  16
  • Frank Perez  · 技术社区  · 7 年前

    好了,伙计们,今天的目标是建立一个图灵机器模拟器。对于那些不知道它是什么的人,看 the Wikipedia article . 我们今天使用的状态表位于 the Formal Definition that's part of that page .

    代码将采用“0”和“1”字符串字符的序列,一个表示机器开始的字符的整数,一个表示程序状态的整数(无特定顺序),并输出字符串操作的最终结果以及最终位置。实例:

    例1:

    1010 state A(0)
       ^ (3)
    1011 state B(1)
      ^ (2)
    1011 state B(1)
     ^ (1)
    1111 state A(0)
      ^ (2)
    1111 state C(0)
       ^ (3)
    1111 HALT
      ^ (2)
    

    例2:

    110100 state B(1)
       ^ (3)
    110100 state B(1)
      ^ (2)
    111100 state A(0)
       ^ (3)
    111100 state C(2)
        ^ (4)
    111110 state B(1)
         ^ (5)
    1111110 state A(0)
          ^ (6, tape has been extended to right)
    1111111 state B(1)
         ^ (5)
    1111111 state B(1)
        ^ (4)
    1111111 state B(1)
       ^ (3)
    1111111 state B(1)
      ^ (2)
    1111111 state B(1)
     ^ (1)
    1111111 state B(1)
    ^ (0)
    01111111 state B(1)
    ^ (0, tape has been extended to left)
    11111111 state A(0)
     ^ (1)
    11111111 state C(2)
      ^ (2)
    11111111 HALT
     ^ (1)
    

    其他:

    • 您的代码必须通过根据需要扩展字符串来正确地处理写入磁带上“空白”的尝试。
    • 因为指定的状态机没有指定任何类型的“空白磁带”操作,所以将所有空白值都视为0。
    • 您必须只计算处理初始状态字符串计算的方法,以及数据的输出方式。
    • 在磁带上向右移动是递增的(字符串位置0一直在左边),状态0是A,状态1是B,状态2是C。

    (希望)最终编辑: 对于这个问题给我带来的混乱和麻烦,我表示最真诚的歉意:我错误地阅读了列出的状态表,并把它倒过来了。我希望你原谅我浪费你的时间,那完全是无意的!

    12 回复  |  直到 11 年前
        1
  •  6
  •   kriss    16 年前

    Perl函数101 char

    sub f{($_,$S,$p)=@_;for(%h=map{$i++,$_}split//;7^$S;$p-=$S<=>3){$S=7&236053>>3*($S%4*2+!!$h{$p}++)}};
    
    f(@ARGV);
    @allpos = sort keys %h;
    for (@allpos){
        print $h{$_}?1:0;
    }
    print " H ".($p-$allpos[0])."\n";
    

    这个很有趣。两个把戏。它对磁带使用哈希,知道吗?哈希是自动扩展的,因此不需要再关心磁带边界了。另一个技巧是将访问的单元的读写结合起来。只需更改内部约定0,空格表示0,任何其他值表示1。这两个技巧意味着对输出进行一些细微的解码,但我相信这是可以的。我也没有在我的函数中计算最后的分号,因为gnibler没有在他的golfscript中计算他的。

    如果有人感兴趣,我也可以发布我的其他尝试。他们有点长,但使用有趣的技巧。例如,一种是基于regex的,直接使用磁带作为字符串;另一种是位fu。

    Perl函数112 char

    sub f{($_,$S,$p)=@_;for(split//;7^$S;@_=($p=0,@_)if($p-=$S<=>3)<0){$S=7&236053>>3*($S%4*2+$_[$p]);$_[$p]=1}@_};
    
    @res = f@ARGV;
    print @res," H $p\n";
    

    我只计算了函数,它按照指定的顺序接受一个字符串、一个状态编号和一个位置。函数以数组形式返回新的磁带状态。

    另一个变体106字符

    sub f{($_,$S,$p)=@_;for(split//;7^$S;$p-=$S<=>3){$S=7&236053>>($S%4*6+$_[$p]*3);$_[$p++]=1;@_=(0,@_)}@_};`
    
    @res = f(@ARGV);
    print @res," H $p\n";
    

    目前还不清楚这是不是作弊。它给出了正确的结果并自动扩展磁带(没有固定的限制),但为了避免在必要或不需要扩展磁带时进行测试,它会执行每个步骤并调整索引。

    另一个变体98字符

    这个也在合并中,但方式不同。它只使用globals在函数内部传递参数。因此,可以在函数外部而不是内部设置变量。从而从函数体中删除14个字符。

    sub f{for(split//;7^$S;@_=($p=0,@_)if($p-=$S<=>3)<0){$S=7&236053>>3*($S%4*2+$_[$p]);$_[$p]=1}@_};
    
    ($_,$S,$p) = @ARGV;
    @res = f();
    print @res," H $p\n";
    
        2
  •  10
  •   gnibbler    16 年前

    python-133个字符

    至少要打败Perl一段时间:)

    def f(t,i,s):
     t=map(int,t) 
     while s<3:t=[0]*-i+t+[0][:i>=len(t)];i*=i>0;c,t[i]=s*4+t[i]*2,1;i+=1-(2&2178>>c);s=3&3401>>c
     return t,i
    

    python-172个字符

    def f(t,i,s):
     t=map(int,t)
     while s<3:
      t=[0]*-i+t+[0]*(i-len(t)+1);i=max(0,i);c,t[i]=t[i],1;i,s=[[(i-1,1),(i+1,2)],[(i+1,0),(i-1,s)],[(i+1,1),(i-1,3)]][s][c]
     return t,i
    

    测试实例

    assert f("1010",3,0) == ([1, 1, 1, 1], 2)
    assert f("110100",3,1) == ([1, 1, 1, 1, 1, 1, 1, 1], 1)
    
        3
  •  9
  •   Aaron    16 年前

    C— 二百八十二 44岁 98字符 (包括所有内部循环变量和表声明)

    #include<stdio.h>
    #include<string.h>
    
    char*S="  A2C1C2  C3A2A0";
    f(char*p,char c){char*e;while(c){e=S+*p*8+c*2;*p=1;p+=*e++-66;c=*e-48;}}
    
    char T[1000];
    main()
    {
      char *p;
            char c;
            char *e;
    
        int initial;
        scanf("%s %d %c",&T[500],&initial,&c);
        c = c - '0' + 1;
    
        for(p=&T[500]; *p; p++)
            *p -= '0';
    
        p = &T[500+initial];
    
        f(p, c);
    
        char *left = T;
        while((left < T+500)&&(!*left))
            left++;
    
        char *right = T+sizeof(T)-1;
        while((right > T+500)&&(!*right))
            right--;
    
        initial = p - left;
    
        for(p=left; p<=right; p++)
            *p+='0';
    
        printf("%.*s %d\n\n",right-left+1,left,initial);
    }
    
        4
  •  3
  •   Guffa    16 年前

    C-157个字符

    void T(List<int>t,ref int p,int s){while(s!=3){if(p<0)t.Insert(0,p=0);if(p==t.Count)t.Add(0);var c=t[p]==1;t[p]=1;p+=s==0==c?1:-1;s=s==1==c?1:c?s==0?2:3:0;}}
    

    该方法采用 List<int> 作为磁带,所以只要内存允许它就可以扩展。

    断言:

    List<int> tape;
    int pos;
    
    tape = "1010".Select(c => c - '0').ToList();
    pos = 3;
    T(tape, ref pos, 0);
    Debug.Assert(String.Concat(tape.Select(n => n.ToString()).ToArray()) == "1111" && pos == 2);
    
    tape = "110100".Select(c => c - '0').ToList();
    pos = 3;
    T(tape, ref pos, 1);
    Debug.Assert(String.Concat(tape.Select(n => n.ToString()).ToArray()) == "11111111" && pos == 1);
    

    如果我们从一开始就欺骗并分配足够大的数组, 107字 :

    void X(int[]t,ref int p,int s){while(s!=3){var c=t[p]==1;t[p]=1;p+=s==0==c?1:-1;s=s==1==c?1:c?s==0?2:3:0;}}
    
        5
  •  3
  •   kriss    16 年前

    Perl142字符(不包括在命令行和最终打印中读取参数的次数)。好吧,大部分代码是beaver程序,引擎本身只有46个字符。

    我改变了输入格式,把状态放在字符串的位置上。我一点也不觉得内疚,因为当head不在字符串中时,大多数代码将成为边界管理。即使在这个版本中,字符串边界管理也要花费17个字符…诀窍是记住你可以把图灵机表示为马尔可夫链…我对正则表达式所做的。

    perl -e '$b=shift;%p=qw(A0|A$ 1B ^A1|0A1 C01 1A1 C11 0B0|^B0 A01 1B0|1B$ A11 B1 1B 0C0|^C0 B01 1C0|1C$ B11 C1 1H);while($b!~/H/){$b=~s/$_/$p{$_}/for keys%p}print"$b\n"' 00A1011
    

    111H1111

    注:事实上,这还不是真正的高尔富德,只是一个幼稚的第一次尝试。我可能会带一些很短的东西回来。

        6
  •  3
  •   gnibbler    16 年前

    golfscript-102个字符

    {:s;{\:$;:^0<{0.:^$+:$}{^$}if.,@>!'0'*+.^=1&s.++:c;.^<1+\^)>+:$[^(^).^(^)^(]c=:^3"120113"c=3&:s-}do}:f
    
    ;
    ["1010" 3 0 f]p
    ["110100" 3 1 f]p
    ["1000000" 3 1 f]p
    

    106字

    {:s;\:$;:i{0<{0.:i$+:$}{i$}if.,@>!'0'*+.i=1&s.++:c;.i<1+\i)>+:$;[i(i).i(i)i(]c=:i 3"120113"c=3&:s-}do$\}:f
    

    113字
    从stdin读取整个程序

    ' '/(:$;(~:i;~~:s;{0i>{0.:i$+:$}{i$}if.,@>!'0'*+.i=1&s.++:c;.i<1+\i)>+:$;[i(i).i(i)i(]c=:i;3"120113"c=3&:s-}do$`i
    

    实例

    $ echo -n 1010 3 0 |../golfscript.rb turing.gs 
    "1111"2
    $ echo -n 110100 3 1 |../golfscript.rb turing.gs 
    "11111111"1
    
        7
  •  2
  •   Graphics Noob    16 年前

    为了澄清这一点,这个程序模拟了忙碌的海狸图灵机器,正如维基百科文章中所描述的那样,而不是操作(操作有R和L切换)

    Python 255字符

    def f(k,i,s):
     t=map(int,k)
     while s<3:
        if i==len(t):t+=[0]
        if i<0:t=[0]+t;i=0
        x=t[i],s
        if x==(0,0):t[i]=1;i-=1;s=1
        if x==(0,1):t[i]=1;i+=1;s=0
        if x==(0,2):t[i]=1;i+=1;s=1
        if x==(1,0):i+=1;s=2
        if x==(1,1):i-=1;s=1
        if x==(1,2):i-=1;s=3
     return t,i
    
        8
  •  2
  •   user231426    16 年前

    Perl,97(实际上是96,因为final“;”对于子块是可选的)

    sub f{($_,$a,pos)=@_;s/\G./$&+2*$a+2/e;1while s!(.?)(2|5)|(3|4|6)(.?)!$2?4+$1.1:8+$4+$3+5*/3/!e}
    f@ARGV;
    #output
    s/7/1/;print;print " H ",(-1+length$`);
    

    想法: $变量包含0和1,但在头下除外。 头下, 0在状态下给出2, 1在一个状态下给出3, 在B状态下0给出4, 1在B状态下给出5, C状态下0给出6, 1在C状态下给出7。

    因此,在第一个示例“1010”(位置3,状态A)之后,给出“1051”,然后给出“1411”、“1131”、“1117”(状态C,位置3),并停止(加上将磁带向右移动)。

        9
  •  1
  •   Frank Perez    16 年前

    Lua:

    半高尔夫版本:

    a=arg
    t=a[1]
    i=a[2]+1
    s=a[3]+0
    r=string.rep
    b=string.sub;z="0";o="1";while true do if i<1 then
            t=z..t
            i=1
        elseif i>#t then
            t=t..z
        end
        c=b(t,i,i)
        if i>0 then
            t=b(t,0,i-1)..o..b(t,i+1,#t)
        else
            t="1"..b(t,i+1,#t)
        end
        if s==0 then
            if c==z then
                i=i-1
                s=1
            elseif c==o then
                i=i+1
                s=2
            end
        elseif s==1 then
            if c==z then
                i=i+1
                s=0
            elseif c==o then
                i=i-1
            end
        elseif s==2 then
            if c==z then
                i=i+1
                s=1
            elseif c==o then
                i=i-1
                break
            end
        end
    end
    print(t,i-1)
    

    压实版本称重 四百四十一 字符:

    a=arg t=a[1] i=a[2]+1 s=a[3]+0 r=string.rep b=string.sub;z="0";o="1";while true do if i<1 then t=z..t i=1 elseif i>#t then t=t..z end c=b(t,i,i) if i>0 then t=b(t,0,i-1)..o..b(t,i+1,#t) else t="1"..b(t,i+1,#t) end if s==0 then if c==z then i=i-1 s=1 elseif c==o then i=i+1 s=2 end elseif s==1 then if c==z then i=i+1 s=0 elseif c==o then i=i-1 end elseif s==2 then if c==z then i=i+1 s=1 elseif c==o then i=i-1 break end end end print(t,i-1)
    

    以磁带、指令指针和状态的形式传递参数,如下所示:

    turing.lua 1010 3 0
    
        10
  •  1
  •   gwell    9 年前

    卢亚,232

    现在使用表查找。

    j={{-1,1},{1,-1},{1,-1}}u={{1,2},{-1,0},{-1,1}}t,i,s=...i=i+1
    s=s+1 z="0"o="1"while s<4 do if i<1 then t=z..t i=1
    elseif i>#t then t=t..z end c=t:sub(i,i):byte()-47
    t=t:sub(0,i-1)..o..t:sub(i+1)i=i+j[s][c]s=s+u[s][c]end print(t,i-1)
    

    这只是 RCIX's answer 再高富,332个字符。

    t,i,s=...i=i+1 s=s+0 r=string.rep b=string.sub z="0"o="1"while s<3 do if i<1 then
    t=z..t i=1 elseif i>#t then t=t..z end c=b(t,i,i)t=b(t,0,i-1)..o..b(t,i+1,#t)if
    s<1 then i=i+(c==o and 1 or -1)s=c==z and 1 or 2 elseif s<2 then i=i+(c==o and
    -1 or 1)s=c==z and 0 or s else i=i+(c==o and -1 or 1)s=c==z and 1 or 3 end end
    print(t,i-1)
    
    • 使用 ... 分配输入参数的运算符
    • 使用 and or 而不是 if 简短时的陈述
    • 替换一些 elseif 用公正 else 通过假设有效的输入/状态
    • 删除parens、椭圆运算符和右引号后的空格
        11
  •  1
  •   Bill Forster    16 年前

    F-275个字符

    好吧,当然不是最短的,而是学习。如果有人可以帮助获取string.mapi以使用 function 而不是 fun match with 我会很感激的,我一直得到“模式鉴别器X没有定义”。任何人都知道一个详细说明使用规则的网站 功能 lambda中的关键字?

    let rec t s i p=
        match s with
        |3->(p,i)
        |_->let g=[[(1,1);(-1,2)];[(-1,0);(1,1)];[(-1,1);(1,3)]]
            let p=match i with|_ when i<0 ->"0"+p|_ when i=p.Length->p+"0"|_->p
            let i=max 0 i
            let m,n=g.Item(s).Item((int p.[i])-48)
            String.mapi(fun x c->match x with|_ when x=i->'1'|_->c) p |> t n (i+m)
    

    用法

    t 1 2 "101011" |> printfn "%A"
    

    以下是用于可读性的扩展版本:

    let rec tur state index tape =
        printfn "Index %d: State %d: Tape %s:" index state tape
        match state with
        |3 -> (tape, index)
        |_ -> let prog = [[(1,1);(-1,2)];[(-1,0);(1,1)];[(-1,1);(1,3)]]
              let tape = match index with |_ when index<0 ->"0"+tape |_ when index=tape.Length->tape+"0" |_->tape
              let index = max 0 index
              let move,newstate = prog.Item(state).Item((int tape.[index])-48)
              String.mapi (fun i c -> match i with |_ when i=index->'1' |_->c) tape
              |> tur newstate (index+move)
    

    我还试图想出一种更好的方法来处理对字符串的操作,而不是 String.mapi . 欢迎并鼓励评论和建议(建设性的请)。

        12
  •  0
  •   DigitalRoss    16 年前

    红宝石,129

    (删除缩进时)

    def pr t,z,s      # DEBUG
      p [t,s]     # DEBUG
      p [' '*z + '^'] # DEBUG
    end       # DEBUG
    
    
    def q t,z,s
      s*=2
      (t=t.ljust z+1
        (t=' '+t;z=0)if z<0
        a=t[z]&1
        t[z]=?1
        b=s>0?1-a: a
        s="240226"[s|a]&7
        z+=b*2-1)while s!=6
      [t,z]
    end
    
    p q "1010",3,0
    p q "110100",3,1
    
    推荐文章