【问题标题】:Code Golf: Automata代码高尔夫:自动机
【发布时间】:2010-10-24 07:59:34
【问题描述】:

我使用这些规则制作了终极笑声发生器。你能用你最喜欢的语言巧妙地实现它吗?

规则:

在每次迭代中,都会发生以下转换。

H   -> AH
A   -> HA
AA  -> HA
HH  -> AH
AAH -> HA
HAA -> AH

n = 0 |  H
n = 1 |  AH
n = 2 |  HAAH
n = 3 |  AHAH
n = 4 |  HAAHHAAH
n = 5 |  AHAHHA
n = 6 |  HAAHHAAHHA
n = 7 |  AHAHHAAHHA
n = 8 |  HAAHHAAHHAAHHA
n = 9 |  AHAHHAAHAHHA
n = ...

【问题讨论】:

标签: puzzle code-golf rosetta-stone automata


【解决方案1】:

F#:184 个字符

似乎很干净地映射到 F#:

type grammar = H | A
let rec laugh = function
    | 0,l -> l
    | n,l ->
        let rec loop = function
            |H::A::A::x|H::H::x|H::x->A::H::loop x
            |A::A::H::x|A::A::x|A::x->H::A::loop x
            |x->x
        laugh(n-1,loop l)

这是在 fsi 中运行:

> [for a in 0 .. 9 -> a, laugh(a, [H])] |> Seq.iter (fun (a, b) -> printfn "n = %i: %A" a b);;
n = 0: [H]
n = 1: [A; H]
n = 2: [H; A; A; H]
n = 3: [A; H; A; H]
n = 4: [H; A; A; H; H; A; A; H]
n = 5: [A; H; A; H; H; A]
n = 6: [H; A; A; H; H; A; A; H; H; A]
n = 7: [A; H; A; H; H; A; A; H; H; A]
n = 8: [H; A; A; H; H; A; A; H; H; A; A; H; H; A]
n = 9: [A; H; A; H; H; A; A; H; A; H; H; A]

【讨论】:

    【解决方案2】:

    REBOL,150 个字符。不幸的是,REBOL 不是一种有助于打代码的语言,但正如 Adam Sandler 所说,150 个字符并不算太简陋。

    这假定循环变量m 已经定义。

    s: "H" r: "" z:[some[["HAA"|"HH"|"H"](追加 r "AH")|["AAH"|"AA"|"A" ](append r "HA")]to end]repeat nm[clear r parse sz print["n = n "|" s: 复制 r]]

    这里有更好的布局:

    s:“H”
    r:“”
    z:[
        一些 [
            [“哈哈”| “哈” | "H" ] (附加 r "AH")
        | [“啊”| “AA” | "A" ] (附加 r "HA")
        ]
        结束
    ]
    
    重复 n m [
        清除 r
        解析 s z
        打印 ["n = n "|" s: 复制 r]
    ]

    【讨论】:

      【解决方案3】:

      Python(150 字节)

      import re
      N = 10
      s = "H"
      for n in range(N):
          print "n = %d |"% n, s
          s = re.sub("(HAA|HH|H)|AAH|AA|A", lambda m: m.group(1) and "AH" or "HA",s)
      

      输出

      n = 0 | H n = 1 |啊 n = 2 |哈哈 n = 3 |啊啊 n = 4 |哈哈哈 n = 5 |啊哈哈 n = 6 |哈哈哈哈 n = 7 |啊啊啊啊 n = 8 |哈哈哈哈哈哈 n = 9 |啊啊啊啊啊

      【讨论】:

        【解决方案4】:

        这是一个 C# 示例,如果我将空格减少到每个项目之间的一个空格,则会出现 321 个字节。

        编辑:作为对@Johannes Rössel 评论的回应,我从解决方案中删除了泛型以获取更多字节。

        编辑:另一个变化,去掉了所有的临时变量。

        public static String E(String i)
        {
            return new Regex("HAA|AAH|HH|AA|A|H").Replace(i, 
                m => (String)new Hashtable {
                    { "H", "AH" },
                    { "A", "HA" },
                    { "AA", "HA" },
                    { "HH", "AH" },
                    { "AAH", "HA" },
                    { "HAA", "AH" }
                }[m.Value]);
        }
        

        使用较少空格的重写解决方案仍然可以编译,是 158 个字符:

        return new Regex("HAA|AAH|HH|AA|A|H").Replace(i,m =>(String)new Hashtable{{"H","AH"},{"A","HA"},{"AA","HA"},{"HH","AH"},{"AAH","HA"},{"HAA","AH"}}[m.Value]);
        

        对于 Visual Studio 2008 的完整源代码解决方案,下面提供了一个包含必要代码(包括单元测试)的 subversion 存储库。

        Repository is here,用户名和密码都是'guest',没有引号。

        【讨论】:

        • 你不能让 Dictionary 非泛型并以这种方式保存几个字节吗? (以牺牲运行时性能为代价)
        • 将方法名称更改为“E”,然后再保存 9 个。
        • 我还删除了临时变量,减少到 239 个字符。
        • 用替换而不是 string.join 降低到 158
        【解决方案5】:

        MATLAB (v7.8.0):

        73 个字符(不包括用于使其看起来可读的格式化字符)

        这个脚本(“haha.m”)假设你已经定义了变量n

        s = 'H';
        for i = 1:n,
          s = regexprep(s,'(H)(H|AA)?|(A)(AH)?','${[137-$1 $1]}');
        end
        

        ...这是单行版本:

        s='H';for i=1:n,s = regexprep(s,'(H)(H|AA)?|(A)(AH)?','${[137-$1 $1]}');end
        

        测试:

        >> for n=0:10, haha; disp([num2str(n) ': ' s]); end
        0: H
        1: AH
        2: HAAH
        3: AHAH
        4: HAAHHAAH
        5: AHAHHA
        6: HAAHHAAHHA
        7: AHAHHAAHHA
        8: HAAHHAAHHAAHHA
        9: AHAHHAAHAHHA
        10: HAAHHAAHHAHAAHHA
        

        【讨论】:

        • 嗯,这似乎是迄今为止最短的,所以我接受了。我从来不知道matlab代码可以这么精简。我为所有努力的人投了赞成票。
        【解决方案6】:

        Perl 168 个字符。

        (不包括不必要的换行符)

        perl -E'
        ($s,%m)=qw[H H AH A HA AA HA HH AH AAH HA HAA AH];
        sub p{say qq[n = $_[0] |  $_[1]]};p(0,$s);
        for(1..9){$s=~s/(H(AA|H)?|A(AH?)?)/$m{$1}/g;p($_,$s)}
        say q[n = ...]'
        

        去混淆:

        use strict;
        use warnings;
        use 5.010;
        
        my $str = 'H';
        
        my %map = (
            H => 'AH',
            A => 'HA',
           AA => 'HA',
           HH => 'AH',
          AAH => 'HA',
          HAA => 'AH'
        );
        
        sub prn{
         my( $n, $str ) = @_;
         say "n = $n |  $str"
        }
        
        prn( 0, $str );
        
        for my $i ( 1..9 ){
          $str =~ s(
            (
              H(?:AA|H)? # HAA | HH | H
            |
              A(?:AH?)?  # AAH | AA | A
            )
          ){
            $map{$1}
          }xge;
        
          prn( $i, $str );
        }
        
        say 'n = ...';
        

        Perl 150 个字符。

        (不包括不必要的换行符)

        perl -E'
        $s="H";
        sub p{say qq[n = $_[0] |  $_[1]]};p(0,$s);
        for(1..9){$s=~s/(?|(H)(?:AA|H)?|(A)(?:AH?)?)/("H"eq$1?"A":"H").$1/eg;p($_,$s)}
        say q[n = ...]'
        

        去混淆

        #! /usr/bin/env perl
        use strict;
        use warnings;
        use 5.010;
        
        my $str = 'H';
        
        sub prn{
         my( $n, $str ) = @_;
         say "n = $n |  $str"
        }
        
        prn( 0, $str );
        
        for my $i ( 1..9 ){
          $str =~ s{(?|
                (H)(?:AA|H)? # HAA | HH | H
              |
                (A)(?:AH?)?  # AAH | AA | A
            )}{
              ( 'H' eq $1 ?'A' :'H' ).$1
            }egx;
          prn( $i, $str );
        }
        
        say 'n = ...';
        

        【讨论】:

          【解决方案7】:

          二郎

          241 字节并准备好运行:

          > erl -noshell -s g i -s init stop
          AHAHHAAHAHHA
          
          -module(g).
          -export([i/0]).
          c("HAA"++T)->"AH"++c(T);
          c("AAH"++T)->"HA"++c(T);
          c("HH"++T)->"AH"++c(T);
          c("AA"++T)->"HA"++c(T);
          c("A"++T)->"HA"++c(T);
          c("H"++T)->"AH"++c(T);
          c([])->[].
          i(0,L)->L;
          i(N,L)->i(N-1,c(L)).
          i()->io:format(i(9,"H"))
          

          可能会改进。

          【讨论】:

            【解决方案8】:

            红宝石

            这个代码高尔夫没有很好地指定——我假设函数返回 n-th 迭代字符串是解决它的最佳方法。它有 80 个字符。

            def f n
            a='h'
            n.times{a.gsub!(/(h(h|aa)?)|(a(ah?)?)/){$1.nil?? "ha":"ah"}}
            a
            end
            

            代码打印出 n 个第一个字符串(71 个字符):

            a='h';n.times{puts a.gsub!(/(h(h|aa)?)|(a(ah?)?)/){$1.nil?? "ha":"ah"}}
            

            【讨论】:

              【解决方案9】:

              在python中:

              def l(s):
               H=['HAA','HH','H','AAH','AA','A']
               L=['AH']*3+['HA']*3
               for i in [3,2,1]:
                if s[:i] in H: return L[H.index(s[:i])]+l(s[i:])
               return s
              
              def a(n,s='H'):
               return s*(n<1)or a(n-1,l(s))
              
              for i in xrange(0,10):
               print '%d: %s'%(i,a(i))
              

              第一次尝试:198 个字符的代码,我相信它可以变得更小:D

              【讨论】:

                【解决方案10】:

                ANSI C99

                进入残酷的 306 个字符:

                #include <stdio.h>
                #include <string.h>
                char s[99]="H",t[99]={0};int main(){for(int n=0;n<10;n++){int i=0,j=strlen(s);printf("n = %u |  %s\n",n,s);strcpy(t,s);s[0]=0;for(;i<j;){if(t[i++]=='H'){t[i]=='H'?i++:t[i+1]=='A'?i+=2:1;strcat(s,"AH");}else{t[i]=='A'?i+=1+(t[i+1]=='H'):1;strcat(s,"HA");}}}return 0;}
                

                嵌套的 if 和条件运算符太多了,我无法通过宏有效地减少这种情况。相信我,我试过了。可读版本:

                #include <stdio.h>
                #include <string.h>
                char s[99] = "H", t[99] = {0};
                int main()
                {
                    for(int n = 0; n < 10; n++)
                      {
                        int i = 0, j = strlen(s);
                        printf("n = %u |  %s\n", n, s);
                        strcpy(t, s);
                        s[0] = 0;
                        /*
                         * This was originally just a while() loop.
                         * I tried to make it shorter by making it a for() loop.
                         * I failed.
                         * I kept the for() loop because it looked uglier than a while() loop.
                         * This is code golf.
                         */
                        for(;i<j;)
                          {
                            if(t[i++] == 'H' )
                              {
                                // t[i] == 'H' ? i++ : t[i+1] == 'A' ? i+=2 : 1;
                                // Oh, ternary ?:, how do I love thee?
                                if(t[i] == 'H')
                                    i++;
                                else if(t[i+1] == 'A')
                                    i+= 2;
                                strcat(s, "AH");
                              }
                            else
                              {
                                // t[i] == 'A' ? i += 1 + (t[i + 1] == 'H') : 1;
                                if(t[i] == 'A')
                                    if(t[++i] == 'H')
                                        i++;
                                strcat(s, "HA");
                              }
                          }
                      }
                    return 0;
                }
                

                将来我也许可以用strncmp() 制作一个更短的版本,但谁知道呢?我们会看看会发生什么。

                【讨论】:

                  【解决方案11】:

                  Lex/Flex

                  69 个字符。在此处的文本中,我将制表符更改为 8 个空格,这样看起来正确,但所有这些连续的空格都应该是制表符,并且制表符很重要,所以它变成了 69 个字符。

                          #include <stdio.h>
                  %%
                  HAA|HH|H        printf("AH");
                  AAH|AA|A        printf("HA");
                  

                  不管怎样,生成的lex.yy.c 是 42736 个字符,但我认为这并不重要。我可以(并且很快会)编写一个更短的纯 C 版本并且做同样的事情,但我觉得这可能应该是一个单独的条目。

                  编辑:

                  这是一个更合法的 Lex/Flex 条目(302 个字符):

                          char*c,*t;
                          #define s(a) t=c?realloc(c,strlen(c)+3):calloc(3,1);if(t)c=t,strcat(c,#a);
                  %%
                          free(c);c=NULL;
                  HAA|HH|H        s(AH)
                  AAH|AA|A        s(HA)
                  %%
                  int main(void){c=calloc(2,1);if(!c)return 1;*c='H';for(int n=0;n<10;n++)printf("n = %d |  %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;}
                  

                  这会进行多次迭代(与上一次不同,它只进行一次迭代,每次都必须手动播种,但会产生正确的结果),并且具有看起来非常可怕的代码的优势。我使用一个函数宏、字符串化运算符和两个全局变量。如果您想要一个甚至不检查 malloc() 故障的更混乱的版本,它看起来像这样(282 个字符):

                          char*c,*t;
                          #define s(a) t=c?realloc(c,strlen(c)+3):calloc(3,1);c=t;strcat(c,#a);
                  %%
                          free(c);c=NULL;
                  HAA|HH|H        s(AH)
                  AAH|AA|A        s(HA)
                  %%
                  int main(void){c=calloc(2,1);*c='H';for(int n=0;n<10;n++)printf("n = %d |  %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;}
                  

                  还可以编造一个更糟糕的版本,其中 c 是堆栈上的一个数组,我们只是给它一个 MAX_BUFFER_SIZE 某种形式,但我觉得这太过分了。

                  ...开个玩笑。 207 个字符,如果我们采用“99 个字符总是就足够了”的心态:

                          char c[99]="H";
                  %%
                          c[0]=0;
                  HAA|HH|H    strcat(c, "AH");
                  AAH|AA|A    strcat(c, "HA");
                  %%
                  int main(void){for(int n=0;n<10;n++)printf("n = %d |  %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;}
                  

                  我偏爱效果最好的那个(即第一个可以迭代直到内存耗尽并检查其错误的那个),但这是代码高尔夫。

                  要编译第一个,输入:

                  flex golf.l
                  gcc -ll lex.yy.c
                  

                  (如果您有lex 而不是flex,只需将flex 更改为lex。它们应该是兼容的。)

                  要编译其他的,请输入:

                  flex golf.l
                  gcc -std=c99 lex.yy.c
                  

                  否则 GCC 会抱怨 ‘for’ loop initial declaration used outside C99 mode 和其他废话。

                  纯 C 答案即将出现。

                  【讨论】:

                  • 我必须承认我对 Lex 不熟悉,所以我想知道 A)您的代码如何通过将字符串初始化为“H”和 B)如何让它停止在给定的 n?
                  • 我很懒——代码只进行了一次迭代。我很快就会想出如何写一个更好的版本,但是现在你输入“H”它会吐出“AH”,所以你输入“AH”它会吐出“HAAH”,等等。有时我会在适当的解决方案中进行编辑,这非常复杂。无论如何,要编译此代码,请键入“flex file.l”,这将生成文件“lex.yy.c”。用 C 编译器编译它,并链接到 lex 库(很可能是 libl.a。)
                  【解决方案12】:

                  Javascript:

                  120 剥离空白,我现在就不管它!

                  function f(n,s){s='H';while(n--){s=s.replace(/HAA|AAH|HH?|AA?/g,function(a){return a.match(/^H/)?'AH':'HA'});};return s}
                  

                  扩展:

                  function f(n,s)
                  {
                      s = 'H';
                      while (n--)
                      {
                          s = s.replace(/HAA|AAH|HH?|AA?/g, function(a) { return a.match(/^H/) ? 'AH' : 'HA' } );
                      };
                      return s
                  }
                  

                  那个替代品很贵!

                  【讨论】:

                    【解决方案13】:

                    对 Haskell 的简单翻译:

                    grammar = iterate step
                        where
                            step ('H':'A':'A':xs) = 'A':'H':step xs
                            step ('A':'A':'H':xs) = 'H':'A':step xs
                            step ('A':'A':xs) = 'H':'A':step xs
                            step ('H':'H':xs) = 'A':'H':step xs
                            step ('H':xs) = 'A':'H':step xs
                            step ('A':xs) = 'H':'A':step xs
                            step [] = []
                    

                    还有一个更短的版本(122 个字符,优化为三个推导规则 + 基本情况):

                    grammar=iterate s where{i 'H'='A';i 'A'='H';s(n:'A':m:x)|n/=m=m:n:s x;s(n:m:x)|n==m=(i n):n:s x;s(n:x)=(i n):n:s x;s[]=[]}
                    

                    以及对 C++ 的翻译(182 个字符,只进行一次迭代,在命令行上以初始状态调用):

                    #include<cstdio>
                    #define o putchar
                    int main(int,char**v){char*p=v[1];while(*p){p[1]==65&&~*p&p[2]?o(p[2]),o(*p),p+=3:*p==p[1]?o(137-*p++),o(*p++),p:(o(137-*p),o(*p++),p);}return 0;}
                    

                    【讨论】:

                    • 好吧,我的目标是最小尺寸:)
                    【解决方案14】:

                    这是一个非常简单的 C++ 版本:

                    #include <iostream>
                    #include <sstream>
                    using namespace std;
                    
                    #define LINES 10
                    
                    #define put(t) s << t; cout << t
                    
                    #define r1(o,a,c0) \
                        if(c[0]==c0) {put(o); s.unget(); s.unget(); a; continue;}
                    #define r2(o,a,c0,c1) \
                        if(c[0]==c0 && c[1]==c1) {put(o); s.unget(); a; continue;}
                    #define r3(o,a,c0,c1,c2) \
                        if(c[0]==c0 && c[1]==c1 && c[2]==c2) {put(o); a; continue;}
                    
                    int main() {
                        char c[3];
                        stringstream s;
                        put("H\n\n");
                        for(int i=2;i<LINES*2;) {
                            s.read(c,3);
                            r3("AH",,'H','A','A');
                            r3("HA",,'A','A','H');
                            r2("AH",,'H','H');
                            r2("HA",,'A','A');
                            r1("HA",,'A');
                            r1("AH",,'H');
                            r1("\n",i++,'\n');
                        }
                    }
                    

                    这不完全是代码高尔夫(它可以做得更短),但它确实有效。将LINES 更改为您想要打印的行数(注意:它不适用于0)。它将像这样打印输出:

                    H
                    
                    AH
                    
                    HAAH
                    
                    AHAH
                    
                    HAAHHAAH
                    
                    AHAHHA
                    
                    HAAHHAAHHA
                    
                    AHAHHAAHHA
                    
                    HAAHHAAHHAAHHA
                    
                    AHAHHAAHAHHA
                    

                    【讨论】:

                    • 我打算尝试使用 lex/C 以类似的方式执行此操作(我认为这会更容易),但实际上这在 C++ 中非常简单,只需几个宏。跨度>
                    猜你喜欢
                    • 2010-12-19
                    • 2010-12-17
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-12-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-11-28
                    相关资源
                    最近更新 更多