【问题标题】:Code Golf: Regex parser代码高尔夫:正则表达式解析器
【发布时间】:2011-04-01 04:49:15
【问题描述】:

目标

今天的 Code Golf 挑战是用尽可能少的字符创建一个正则表达式解析器。

语法

不,我不是要你匹配 Perl 风格的正则表达式。毕竟,已经有一个非常可靠的口译员了! :-)

以下是您需要了解的有关此挑战的正则表达式语法的所有信息:

  • 术语定义为单个文字字符,或分组括号()内的正则表达式。
  • *(星号)字符表示上一个 TERM 上的 Kleene 星号操作。这意味着前一个术语的零个或多个,连接在一起。
  • +(加号)字符代表一个方便的快捷方式:a+ 等同于 aa*,表示上一个术语的一个或多个。
  • ?(问号)字符表示前一个词的零个或一个。
  • |(竖线)字符表示交替,这意味着任何一方的正则表达式都可以在匹配中使用。
  • 假定所有其他字符都是文字。您可以假设所有其他字符都在 [0-9A-Za-z] 内(即所有英文字母数字)。

或者,换一种说法:*/+/? 具有最高优先级,然后是串联,然后是交替。由于交替的优先级低于串联,因此在不带括号的正则表达式中使用它会导致它被绑定到每一侧的完整正则表达式。另一方面,*+? 仅适用于前一个术语。

挑战

您的挑战是编写一个程序来编译或解释正则表达式(如上定义),然后针对它测试一些字符串。

我将把输入留给你。我的建议是,正则表达式可能应该首先出现,然后是任意数量的要对其进行测试的字符串;但如果你想坚持下去,那很好。如果您想将所有内容放入命令行参数或标准输入中,或者将正则表达式放入命令行中,将字符串放入标准输入中,或者其他任何东西,那很好。只显示一两个使用示例。

输出应为truefalse,每行一个,以反映正则表达式是否匹配。

注意事项:

  • 我不应该这么说...但不要使用您的语言中的任何正则表达式库!您需要自己编译或解释模式。 (编辑:如果需要拆分或连接字符串,您可以使用正则表达式。您不能使用它直接解决问题,例如,将输入正则表达式转换为语言正则表达式并使用它。 )
  • 正则表达式必须与此质询的输入字符串完全匹配。 (等效地,如果您熟悉类似 Perl 的正则表达式,则假设所有匹配项都具有字符串开头和结尾锚定)
  • 对于这个挑战,所有特殊字符 ()*+?| 预计不会按字面意思出现。如果输入中出现了一个,则可以安全地假设没有任何模式可以匹配所讨论的字符串。
  • 应以区分大小写的方式评估要测试的输入字符串。

例子

对于示例,我假设一切都在命令行参数中完成,首先是正则表达式。 (正如我上面所说,输入取决于您。)myregex 这里代表您对程序的调用。

> myregex easy easy Easy hard
true
false
false

> myregex ab*a aa abba abab b
true
true
false
false

> myregex 0*1|10 1 10 0110 00001
true
true
false
true

> myregex 0*(1|1+0) 1 10 0110 00001
true
true
true
true

> myregex a?b+|(a+b|b+a?)+ abb babab aaa aabba a b
true
true
false
true
false
true

注意:抱歉,忘记创建社区 wiki! :-(

【问题讨论】:

  • 这是一个解释器,而不仅仅是一个解析器。
  • 这是一个经过深思熟虑的高尔夫;下班后我会看看使用解析器组合器试试看;)
  • Voters for close... 有什么建议可以让您看起来更像是一个“真正的问题”吗?是的,您需要通读全文,但这绝对是一个问题:谁能用最少的击键次数编写正则表达式解析器/解释器?很简单,真的,即使很难回答。 :-)
  • 我投票结束(而且我通常不会投票结束 Code-Golf 问题),因为这是一个“过于本地化”的问题。您将从了解 Code-golf 并希望花时间在 code-golf 中编写正则表达式机器的那部分人那里得到答案。除了痛苦之外,这不是很有教育意义。最好用code-golf-ese写一个*not,方便其他人学习。
  • @George Stocker:我不敢苟同——编写正则表达式匹配器既有趣又具有教育意义。即使一个人写了 1000+ 个字符中的一个,这仍然是 codegolf 的胜利......因为还没有发布工作的人:)

标签: regex language-agnostic code-golf rosetta-stone


【解决方案1】:

Perl,596 个字符

半解释:

  • 这是基于 Higher Order Perl 第 8 章中的连续传递式解析器组合器。
  • 感谢 Vincent Pit (VPIT) 帮助我删除了大约 70 个字符。
  • S { block }sub { block } 相同,只是每次短 2 个字符。
  • $, 为 nil(包含始终匹配的匹配器的 coderef,不消耗任何内容)
  • c 是 n 元连接(获取任意数量的匹配器,如果它们都按顺序成功,则返回成功的匹配器)。
  • a 是 n 元交替(获取任意数量的匹配器,如果其中任何一个成功,则返回成功的匹配器)。
  • A 是正则表达式构建器的助手——它采用连接交替的结构,并根据需要传递给 Ca,返回一个匹配器。
  • k 是星号(取一个匹配器并返回一个匹配它的匹配器按顺序匹配 0 次或多次。k 用于 Kleene,因为 s() 被解析为 s/// 运算符:)
  • do 块一次解析正则表达式一个字符。 @$r 是当前串联列表,@a 是当前交替列表,@p 是paren-group 堆栈。 a+ 被视为aa*a?b 中被视为(a|) 内联(plusmaybe 没有函数)。
  • 最后一行中的S{!length pop} 是在输入结束时成功的匹配器。它作为正则表达式匹配器的延续传递,这意味着正则表达式只有在它可以匹配整个字符串时才会成功。

对于大部分脱格和更多注释的代码,请参阅this Gist

perl regexer.pl 'a?b+|(a+b|b+a?)+' abb babab aaa aabba a b 运行它。代码中没有强制换行符。

use feature':5.12';
sub S(&){pop}
$,=S{goto pop};
sub p{push@{+shift},@_}
sub c{my$l=$,;for my$r(@_){my$L=$l;
$l=S{my($i,$c)=@_;&$L($i,S{&$r(shift,$c)})}}$l}
sub a{my@A=@_;S{my($i,$c,$o)=@_;$o=&$_($i,$c)and return$o for@A;0}}
sub A{$#_?a(map c(@$_),@_):c(@{+pop})}
sub k{my($I,$k)=@_;$k=a c($I,S{&$k}),$,}
$_=shift;$P=do{@a=$r=[];for(/./g){
when('('){p\@p,[@a];@a=$r=[]}
when(')'){$p=A@a;@a=@{pop@p};p$r=$a[-1],$p}
p\@a,$r=[]when'|';
p$r,k pop@$r when'*';
p$r,c $$r[-1],k pop@$r when'+';
p$r,a pop@$r,$,when '?';
my$s=$_;p$r,S{my($_,$c)=@_;s/^\Q$s//&&$_->$c}}A@a};
say&$P($_,S{!length pop})?"true":"false"for@ARGV

【讨论】:

    【解决方案2】:

    GolfScript - 254 个字符

    n%([]:B:$:_"()"@*{:I"()*+|?"[{}/]?[{[[0B$,+:B))\;)]_]+}{B)):ß;:B;qß(:ß;}{8q}{[[0ß0$,)]]+}:8{[[0B-1=:ß)]]+:$q}{ß>$ß<\([0+$,+]\++}:q{[[I$,:ß)]]+}]=~:$}/;{n+[0]:3\{:c;;3:1_:3;{,}{)[$=]_*2/{~\.{c={3|:3}*;}{;.1|1,\:1,<{+0}*;}if}/}/;}/;1$,?)"true""false"if n}%
    

    有点直截了当,第一个循环将正则表达式转换为 NFA,第二个循环运行该 NFA。

    Sun Aug 22 00:58:24 EST 2010 (271→266) 更改变量名称以删除空格
    Sun Aug 22 01:07:11 EST 2010 (266→265)[] 设为变量
    Sun Aug 22 07:05:50 EST 2010 (265→259) 内联进行空状态转换
    Sun Aug 22 07:19:21 EST 2010 (259→256) 最终状态变为隐式
    Mon Feb 7 19:24:19 EST 2011 (256→254) 使用"()""str"*

    $ echo "ab*a aa abba abab b"|tr " " "\n"|golfscript regex.gs
    true
    true
    false
    false
    
    $ echo "0*1|10 1 10 0110 00001"|tr " " "\n"|golfscript regex.gs
    true
    true
    false
    true
    
    $ echo "0*(1|1+0) 1 10 0110 00001"|tr " " "\n"|golfscript regex.gs
    true
    true
    true
    true
    
    $ echo "a?b+|(a+b|b+a?)+ abb babab aaa aabba a b"|tr " " "\n"|golfscript regex.gs
    true
    true
    false
    true
    false
    true
    
    $ echo "((A|B|C)+(a|(bbbbb)|bb|c)+)+ ABCABCaccabbbbbaACBbbb ABCABCaccabbbbbaACBbbbb"|tr " " "\n"|golfscript regex.gs
    false
    true
    

    【讨论】:

    • 天哪!我的编程技术一下子显得小得可怜,一文不值! =S,你得到了 2^8 个字符!
    【解决方案3】:

    Ruby 1.9:709 个字符

    R=gets.chop;s='';k=[];n=a=0
    G={?(=>(A="(a-=1;s<<0)if a>1;")+"k<<[n,a];n=a=0",
    Y=?|=>(B="s<<0while 0<a-=1;")+"n+=1",
    ?)=>B+(C="s<<?|while 0<=n-=1;")+"n,a=k.pop"+F=";a+=1",
    ?*=>D="s<<c",?+=>D,??=>D}
    R.each_char{|c|eval G[c]||A+D+F};eval B+C
    def P l,s;l.map{|a|a<<s};end
    J={??=>(K="a=k.pop;")+"k<<[{Y=>n=[a[0]]},a[1]<<n]",
    ?*=>K+(H="P a[1],s={Y=>n=[a[0]]};")+"k<<[s,[n]]",
    ?+=>K+H+"k<<[a[0],[n]]",
    Y=>(I=K+"b=k.pop;")+"k<<[{Y=>[a[0],b[0]]},a[1]+b[1]]",
    ?\0=>I+"P b[1],a[0];k<<[b[0],a[1]]"}
    k=[];s.each_char{|c|eval J[c]||"k<<[{c=>a=[]},[a]]"}
    e=k[0];P e[1],R;
    p $<.map{|l|s=l.chop;*a=e[0]
    s.each_char{|c|eval@f="n=a;a=a.map{|h|h[Y]||[h]}.flatten"while a!=n
    a=a.inject([]){|a,h|a+(h[c]||[])}}
    eval@f;a.include? R}
    

    (在 Ruby 1.8 中也可以通过添加下面的别名来增加 45 个字符)

    type testcase.txt | ruby regex.rb测试

    Russ Cox's NFA parser 在 Ruby 中的实现。状态表示为具有单个键的散列,该键包含下一个状态的数组。

    不打高尔夫球:

    #needed to run on ruby 1.8
    class String
      alias :each_char :each_byte 
    end  
    
    ## Infix to Postfix
    
    R=gets.chop  
    p "regexp = #{R}"
    k=[] 
    s=''  #will hold postfix string
    n=a=0 #count braNches and Atoms
    postfix = R.each_char{|c|
      case c
        when ?(
            (a-=1;s<<0)if a>1
            k<<[n,a]
            n=a=0
        when ?|
          s<<0while 0<a-=1
          n+=1
        when ?)
          s<<0while 0<a-=1
          s<<?|while 0<=n-=1
          n,a=k.pop;a+=1
        when ?*,?+,??
          s<< c
        else
          (a-=1;s<<0)if a>1
          s<< c
          a+=1
      end
    }
    s<<0while 0<a-=1
    s<<?|while 0<=n-=1
    
    ## Postfix to NFA
    # State is {char=>s0=[state,state]]
    # Frag is [state, [s0,...]]
    
    Y=?|   #choice indicator
    X={:true=>[R]} #matcstate
    
     def patch l,s   #l is list of arrays, s is state.  Put s in each array
       l.map{|a|   a<< s   }
    end
    
    k=[]
    s.each_char{|c|
     case c
        when ??
          a=k.pop
          s={Y=>n=[a[0]]}
          k<<[s,a[1]<<n]
        when ?*
          a=k.pop
          s={Y=>n=[a[0]]}
          patch(a[1],s)
          k<<[s,[n]]
        when ?+
          a=k.pop
          s={Y=>n=[a[0]]}
          patch(a[1],s)
          k<<[a[0],[n]]
        when ?|
         b=k.pop
         a=k.pop
          s={Y=>[a[0],b[0]]}
          k<<[s,a[1]+b[1]]
        when 0
         b=k.pop
         a=k.pop
         patch(a[1],b[0])
         k<<[a[0],b[1]]
       else
         k<<[{c=>a=[]},[a]]
       end
    }
    e=k.pop
    patch(e[1],X)
    
    #Running the NFA
    
    E=[e[0]] #Evaluator
    p $<.map{|l|s=l.chop
      p "evaluating: #{s}" 
      a=E
      s.each_char{|c|
        begin     #skip past split nodes
          s=a.size
          a=a.map{|h|h[?|]||[h]}.flatten  
        end while a.size!=s
        a=a.inject([]){|a,h|
        a+(h[c]||[])}  #add next state or null
      }
      a=a.map{|h|h[?|]||[h]}.flatten
      r = a.include? X  #check for end of pattern
      p r
      r
    }
    

    【讨论】:

      【解决方案4】:

      JavaScript,658 个字符

      // All whitespace is optional
      function c(f,p){
          var x=f[0],w=p[0],h="substr",s=f[h](2),b=p[h](1),m=0,t=0,r,g,a=0,l,u="length",y="*";
          switch(f[1]){
              case"+":if(x!=w)return;f=x+y+s;
              case y:return x==w&&c(f,b)||c(s,p);
              case"?":return x==w&&c(s,b)||c(s,p)
          }
          if(x=="("){
              o:for(l=[""];t<f[u];t++){
                  switch(f[t]){
                      case"|":if(a==1){m=l.push("")-1;continue}break;
                      case"(":if(++a==1)continue;break;
                      case")":if(!--a)break o
                  }
                  l[m]+=f[t]
              }
              var v=0,e=t+1;
              return l.some(function(g){
                  switch(f[t+1]){
                      case y:v=1;
                      case"+":e=t+2;
                          for(var q=0;q<f[u];q++)
                              if(c(g+Array(q).join(f[h](0,e))+f[h](e),p))
                                  return 1;
                          return;
                      case"?":v=1;e=t+2;default:if(c(g+f[h](e),p))return 1;
                  }
              })||(v&&c(f[h](e),p))
          }
          return p[u]?(x==w&&c(f[h](1),b)):!f[u]
      }
      
      // Make it look nicer
      function test(regex, string) { return !!c('(' + regex + ')', string); }
      
      test('a?b+|(a+b|b+a?)+', 'abb') // true
      test('a?b+|(a+b|b+a?)+', 'babab') // true
      

      Ungolfed,~1500 个字符

      function test(reg, str) {
          console.log('Testing ' + reg + ' against ' + str);
          var c = reg[0], d = str[0];
      
      
          switch (reg[1]) {
              case '+': 
                  if (c != d) 
                      return false;   
                  reg = c + '*' + reg.substr(2);
              case '*': 
                  return (c == d && test(reg, str.substr(1))) || test(reg.substr(2), str);
              case '?': 
                  return (c == d && test(reg.substr(2), str.substr(1))) || test(reg.substr(2), str);
          }
      
          if (c == '(') {
              var regs = [''];
              o: for (var level = n = i = 0; i < reg.length; i++) {
                  //console.log(level + ': ' + n + ': ' + reg[i]);
                  switch (reg[i]) {
                      case '|': 
                          if (level == 1) { n = regs.push('') - 1; continue; } 
                          break;
                      case '(': 
                          if (++level == 1) continue; 
                          break;
                      case ')': 
                          if (!--level) break o; 
                          break;
                  };
                  regs[n] += reg[i];
              }
              //console.log(regs); // An array of alternates (hello|hi) => ['hello', 'hi']        
      
              var optional = false, end = i+1;
              return regs.some(function(jitem) {
                  switch (reg[i+1]) {
                      case '*': optional = true; // Fall through
                      case '+': 
                          end = i+2;
                          for (var k = 0; k < reg.length; k++)
                              if (test(jitem + Array(k).join(reg.substr(0, i+2)) + reg.substr(i+2), str))
                                  return true;
                          return false;
      
                      case '?': optional = true; end = i+2; // Fall through
                      default: if (test(jitem + reg.substr(end), str)) return true;       
                  }
      
              }) || (optional && test(reg.substr(end), str));
          }
      
          if (str == '')
              return reg == '';
      
          return c == d ? test(reg.substr(1), str.substr(1)) : false;
      
      }
      

      这通过递归来工作,通过切断正则表达式的前面和字符串的前面,并调用它自己。例如,test("hello", "hello") =&gt; test("ello", "ello") =&gt; test("llo", "llo") =&gt; test("lo", "lo") =&gt; test("o", "o") =&gt; test("", "") 返回 true。注意:裸c 函数将不支持隐式交替。换句话说,hello|hi 不起作用;它必须用括号括起来:(hello|hi)

      【讨论】:

      • +1 为该解决方案的第 6 个解决方案!为了可读性,您能否将线条包装在打高尔夫球的版本中?只是想知道,我没有测试,但这是否与 (a+)(aaa) 正确匹配 aaaa
      • 它似乎可以工作 99%,但在 ((ab)+)+acabab 上会导致 firefox 崩溃
      【解决方案5】:

      C(解释),630 622 617 615 582 576 573 572 558 554 544 538 529 504 502 500 499 个字符

      这理解括号,并在正则表达式上工作(即不先解析)

      #define C char
      #define M m(s,o
      m(C*s,C*o,C*S,C*p,C*P,C T){C*n=P-1,*q=s,h=*P==41,c=1;for(;h*c;c-=*n--==40)c+=*n==41;
      c=*P-42;c=p-P?c-82?T&&c&~1&&c-21?h?2:*S==*P&s<S?M,S-1,p,n,2)||(T&4&&M,S-1,p,P,T|1)):
      4:M,T?S:o,p,P-1,T?c&~1?3:7-c:0):T&&s==S||M,o,p,P-1,2):T&&s==S;if(c==2)for(c=4;q<=S;q
      ++)c|=m(q,S,S,n+h,P-h,2)?M,q,p,n,2)||q<S&T/4&&M,q,p,P,T&6):0;return
      c&4?c&1?:T&1&&M,S,p,n,2)||M,o,p,n,0):c;}main(C*w,C**v){C*u;for(w=*++v;*++v;)puts(m(*v
      -1,u,u=index(*v,0)-1,w-1,index(w,0)-1,2)?"true":"false");}
      

      用 -Wall 编译会抱怨 argc 是 char。会因非法模式而崩溃。

      这会从右到左解析正则表达式和字符串,节省一些字符。

      在 argv 上输入,以 reverse 正常顺序输出:

      $ ./a.out ab*a aa abba abab b
      true
      true
      false
      false
      
      $ ./a.out "0*1|10" 1 10 0110 00001
      true
      true
      false
      true
      
      $ ./a.out "0*(1|1+0)" 1 10 0110 00001
      true
      true
      true
      true
      
      $ ./a.out "a?b+|(a+b|b+a?)+" abb babab aaa aabba a b
      true
      true
      false
      true
      false
      true
      
      $ ./a.out "((A|B|C)+(a|(bbbbb)|bb|c)+)+" ABCABCaccabbbbbaACBbbb
      false
      $ ./a.out "((A|B|C)+(a|(bbbbb)|bb|c)+)+" ABCABCaccabbbbbaACBbbbb
      true
      

      【讨论】:

      • 目前为止做得很好!我很想看到输出顺序得到纠正——只需要几个字符就可以了,不是吗?但它看起来很不错,我喜欢你添加的替代示例。另外: (a) 不要担心非法模式;你可以假设所有的测试用例都是有效的。 (b) 你对 argv 提出了一个很好的问题......我个人不会相信它:-)
      • 我通过释放现在是指针的 argc 甚至赢得了几个字节,谢谢 ;-)
      • 你用什么编译这个? GCC 为我崩溃,两次抱怨 undefined reference to index
      • @Callum man index 说:符合 4.3BSD;在 POSIX.1-2001 中标记为 LEGACY。 但您可以将其替换为 strchr
      【解决方案6】:

      Haskell:610 612 627

      main=getLine>>=f.words
      d=reverse
      u=0<1
      j=[]
      f(r:s)=mapM_(print.any null.c(d$b$'(':r++")"))s
      c%(x,y)=(c:x,y)
      s _ _ _[]=(j,j)
      s n a b (x:y)|n<1&&x==a=(j,x:y)|x==a=f(-1)|x==b=f 1|u=f 0where f k=x%s(n+k)a b y
      b r|m==j=r|u=b$d(o++"(("++x)++")("++z++")/)"++w where(c,m)=s 0'|''!'r;_:v=m;(o,_:x)=s 0'('')'$d c;(z,_:w)=s 0')''('v
      (!)g f x=f x>>=g
      c[]=(:j)
      c r=f!c s where(s,f)=i r
      p q@(r:s)|r=='('=(s,(:j))|u=(a,f!g)where(w,f)=i q;(a,g)=p w
      _?[]=j
      c?(h:r)|c==h=[r]|u=j
      i(r:q)=maybe(q,(r?))id$lookup r$zip")/*+?"$p q:zip[e,w,w,w][\s->f s++g s,\s->s:l s,l,\s->s:f s]where(w,f)=i q;(e,g)=i w;l s|f s==j=j|u=f s++(f s>>=l)
      

      不打高尔夫球:

      import Control.Monad
      import Data.List
      
      -- (aa|bb|cc)   -->  )|)cc()|)bb()aa((( 
      
      testRegex = "a?b+|(a+b|b+a?)+"
      
      interpret regex = any null . interpret' regex
      
      interpret' regex = compile (rewrite regex)
      
      mapFst f (x, y) = (f x, y)
      
      splitWhileBalanced _ _ _ "" = ("", "")
      splitWhileBalanced n b1 b2 (x:xs)
        | n < 1 && x == b1 = ("", x:xs)
        | x == b1   = f (-1)
        | x == b2   = f 1
        | otherwise = f 0
        where
          f k = mapFst (x:) $ splitWhileBalanced (n+k) b1 b2 xs
      
      rewrite regex = reverse $ moveBars $ '(' : regex ++ ")"
      
      moveBars regex
        | mtBar == "" = regex
        | otherwise = moveBars $ reverse (hOpen ++ "((" ++ tOpen) ++ ")(" ++ hClose ++ ")/)" ++ tClose
        where
          (hBar, mtBar) = splitWhileBalanced 0 '|' '!' regex -- '!' is a dummy character
          b:tBar = mtBar
          (hOpen, o:tOpen) = splitWhileBalanced 0 '(' ')' $ reverse hBar
          (hClose, c:tClose) = splitWhileBalanced 0 ')' '(' $ tBar
      
      compile "" = \x -> [x]
      compile rs = f <=< compile rs'
        where 
          (rs', f) = compile' rs
      
      paren regex@(r:rs0)
        | r == '('  = (rs0, \x -> [x])
        | otherwise = (rs2, f <=< g)
        where
          (rs1, f) = compile' regex
          (rs2, g) = paren rs1
      
      compile' (r:rs0) = case r of
        '/' -> (rs2, bar)
        '*' -> (rs1, star)
        '+' -> (rs1, plus)
        '?' -> (rs1, mark)
        ')' -> paren rs0
        _   -> (rs0, lit)
        where
          (rs1, f) = compile' rs0
          (rs2, g) = compile' rs1
          bar str = f str ++ g str
          plus str
            | null (f str) = []
            | otherwise = f str ++ (f str >>= plus)
          star str = str : plus str
          mark str = str : f str
          lit = maybe [] (\x -> [x]) . stripPrefix [r]
      

      备忘录:

      | 修改为后缀形式,然后反转整个正则表达式。现在运算符采用前缀形式,便于解析。该程序像这样解析正则表达式。 list monad 用于不确定性。

      用法:

      > runghc Regex.hs
      a?b+|(a+b|b+a?)+ abb babab aaa aabba a b
      True
      True
      False
      True
      False
      True
      

      【讨论】:

      • +1 用于扭转问题,也在考虑这个问题! (但您不能保持字符串原样并仅反转 匹配 吗?)
      • +1,非常好。在我正在开发的 Perl 解决方案中,我已将 [+*?|] 移至前缀形式。我仍在努力让[+*?] 工作,因此我还没有发布它。 :-)
      • @mvds 好电话...起初我认为这需要更多的工作,但它只是将 &gt;=&gt; 更改为 &lt;=&lt; :D (-2 chars)
      • @trinithis:我不喜欢 Haskell,但为什么里面还有 reverse 关键字?对于 C 来说,扭转整个事情的好处是(至少)538 上的 34。
      • 这是一个函数,而不是关键字。在任何情况下,一个反转是反转正则表达式以使符号出现在前缀中。 moveBars (ungolfed) 中的反转用于从字符串的结尾而不是开头拆分字符串(一个从结尾匹配,第二个用于撤消第一个反转)。
      【解决方案7】:

      C(解析+匹配)727 670 个字符

      这还没有完全达到最低限度,但我想尝试看看首先解析正则表达式是否会对实时解释它产生影响。确实如此,因为它的成本更高,尽管解析和匹配都更容易编写/理解。

      #define Q struct q
      #define C char
      #define R return
      Q{Q*u,*n,*a;C c,m};Q*P(C*p,C*e){Q*r=calloc(99,1);C*n=p+1,c=1,w;if(p==e)R
      r;if(*p==40){for(;c;)c+=(*n==40)-(*n++==41);r->u=P(p+1,n-1);}else
      if(*p=='|'){r->a=P(p+1,e);R r;}else r->c=*p;if(n<e){if(*n==43)*n=42,r->n=P(p,e);else
      w=*n==42|*n==63,r->n=P(n+w,e),r->m=w?*n:0;r->a=r->n->a;}R r;}M(Q*r,C*s,C*o,C*z){C*p,
      e;e=r?r->m==63|r->m==42&&M(r->n,s,o,z)?:*s&&r->c==*s?M(r->m==42?r:r->n,s+1,o,z):2:s
      ==z;if(e-2)R e;for(p=s,e=0;!r->c&p<=z;p++)e|=M(r->u,s,s,p)&(r->m!=42|p>s)&&M(r->m==
      42?r:r->n,p,p,z);R e||r->a&&M(r->a,o,o,z);}main(C
      c,C**v){for(;--c>1;)puts(M(P(v[1],index(v[1],0)),v[c],v[c],index(v[c],0))?"true":"false");}
      

      它将一个正则表达式解析为一个结构,其中每个结构都有:

      • 要匹配的字符指向要匹配的子模式结构的指针
      • 指向下一个符号结构的指针(如果有)
      • 指向下一个替代模式的指针(如果有)
      • 乘数为 \0、*? -- (pat)+ 立即被解析为 (pat)(pat)*,这使得匹配更加容易

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-03
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        • 1970-01-01
        • 2013-01-21
        • 2010-12-17
        相关资源
        最近更新 更多