<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    用Ruby寫了個NFA

    Posted on 2008-02-25 17:46 dennis 閱讀(1333) 評論(1)  編輯  收藏 所屬分類: 動態語言計算機科學與基礎
        今天有點空閑,想想用Ruby寫個NFA試試。從正則表達式構造NFA采用經典的Thompson算法:正則表達式 -> 后綴表達式 -> 構造NFA。構造了NFA后,用之匹配字符串。一句話,寫了個玩具的正則表達式引擎,支持concatenation、alternation以及*、?、+量詞,不支持反向引用和轉義符。測試了下與Ruby自帶的正則表達式引擎的性能對比,慢了3倍。構造NFA沒什么問題,主要是匹配運行寫的爛,有空再改改。

    nfa.rb

    module NFA
      
    class NFA
        
    def initialize(state)
          @state
    =state
        end
        
    def step(clist,c)
          
    return clist if clist.size==0;
          nlist
    =[] 
          allNull 
    = true
          matched 
    = false
          clist.each do 
    |t|
            
    if !t.nil?
              allNull 
    = false if t.c!=-1
              
    if t.c == c && t.end.type ==1 then
                matched 
    = true
                nlist.push(t.end.out1) 
    if !t.end.out1.end.nil? 
                nlist.push(t.end.out2) 
    if !t.end.out2.end.nil?
              elsif (t.c 
    == c && t.end.type == 0) then
                matched 
    = true;
                
    return ListUitls.new_list(t);
              elsif (t.c 
    == -1 && !t.end.nil?) then
                nlist.push(t.end.out1);
                nlist.push(t.end.out2);
              end
            end
          end        
          
    return step(nlist, c) if (allNull)
          
    return step(nlist, c) if (!matched)
          nlist
        end
        
    def test?(s)
          match(@state,s)
        end
        
    def match(state,s)
          clist 
    =[]
          clist.push(state.out1);
          clist.push(state.out2);
          s.each_byte do 
    |c|
            c 
    =c&0xFF;
            clist 
    = step(clist, c);
            
    return false if clist.size==0
          end
          
    return is_match?(clist)
        end
        
    def is_match?(clist)
          clist.each  do 
    |t|
            
    return true if !t.nil? and t.c==-1 and t.end and t.end.is_matched? 
          end
          false
        end
      end
      
    class Paren
        attr_accessor:n_alt,:n_atom
      end
      
    class State
        attr_accessor :out1,:out2,:type
        
    def initialize(out1,out2)
          @out1
    =out1
          @out2
    =out2
          @type
    =1
        end
        
    def is_matched?
          
    return @type==0
        end
      end
      
    class Transition
        attr_accessor :c,:end
        
    def initialize(c)
          @c
    =c
        end   
      end
      
    class Frame
        attr_accessor :start,:outs
        
    def initialize(start,outs)
          @start
    =start
          @outs
    =outs
        end
      end
      
    class ListUitls
        
    def self.link(list,state)
          list.each{
    |t| t.end=state}
        end
        
    def self.append(list1,list2)
          list1
    +list2
        end
        
    def self.new_list(out)
          result
    =[]
          result.push(out)
          result      
        end
      end
      
    def self.compile(re)
        post 
    = re2post(re)
        
    raise ArgumentError.new,"bad regexp!" if post.nil?
        state 
    = post2nfa(post);
        
    raise RuntimeError.new,"construct nfa from postfix fail!" if state.nil?        
        
    return NFA.new(state);
      end
      
    def self.post2nfa(postfix)
        stack
    =[]
        s
    =nil
        t
    =t1=t2=nil 
        e1
    =e2=e=nil 
        
    return nil if postfix.nil?
        postfix.each_byte do 
    |p|
          case p.chr
          when 
    '.':
            e2 
    = stack.pop() 
            e1 
    = stack.pop() 
            ListUitls.link(e1.outs, e2.start) 
            stack.push(Frame.new(e1.start, e2.outs)) 
          when 
    '|':
            e2 
    = stack.pop() 
            e1 
    = stack.pop() 
            t1 
    = Transition.new(-1)
            t2 
    = Transition.new(-1
            t1.end 
    = e1.start 
            t2.end 
    = e2.start 
            s 
    = State.new(t1, t2) 
            stack.push(Frame.new(s, ListUitls.append(e1.outs, e2.outs))) 
          when 
    '?':
            e 
    = stack.pop() 
            t1 
    = Transition.new(-1)
            t2 
    = Transition.new(-1
            t1.end 
    = e.start 
            s 
    = State.new(t1, t2) 
            stack.push(Frame.new(s, ListUitls.append(e.outs, ListUitls.new_list(t2)))) 
          when 
    '*':
            e 
    = stack.pop() 
            t1 
    = Transition.new(-1)
            t2 
    = Transition.new(-1)
            t1.end 
    = e.start 
            s 
    = State.new(t1, t2) 
            ListUitls.link(e.outs, s) 
            stack.push(Frame.new(s, ListUitls.new_list(s.out2))) 
          when 
    '+':
            e 
    = stack.pop() 
            t1 
    = Transition.new(-1
            t2 
    = Transition.new(-1)
            t1.end 
    = e.start 
            s 
    = State.new(t1, t2) 
            ListUitls.link(e.outs, s) 
            stack.push(Frame.new(e.start, ListUitls.new_list(t2))) 
          
    else
            t 
    = Transition.new(p) 
            s 
    = State.new(t, Transition.new(-1)) 
            stack.push(Frame.new(s, ListUitls.new_list(s.out1))) 
          end
        end
        e 
    = stack.pop() 
        
    return nil if stack.size()>0
        end_state 
    = State.new(nil, nil) 
        end_state.type
    =0
        e.outs.each do 
    |tran|
          
    if tran.c!=-1
            t1 
    = Transition.new(-1)
            t2 
    = Transition.new(-1
            s
    =State.new(t1,t2)
            tran.end
    =s
            s.out1.end
    =end_state
            s.out2.end
    =end_state
          
    else
            tran.end
    =end_state         
          end
        end
        start 
    = e.start 
        
    return start 
      end
      
    def self.re2post(re)
        n_alt 
    = n_atom = 0 
        result
    =""
        paren
    =[]
        re.each_byte do 
    |c|
          case c.chr  
          when 
    '(' then
            
    if (n_atom > 1) then
              n_atom
    -=1 
              result
    <<"."
            end
            p 
    =Paren.new 
            p.n_alt 
    = n_alt 
            p.n_atom 
    = n_atom 
            paren.push(p) 
            n_alt 
    = n_atom = 0
          when 
    '|' then
            
    if (n_atom == 0)
              
    return nil
            end
            
    while (n_atom-=1> 0 
              result
    <<"."
            end
            n_alt
    +=1
          when 
    ')' then
            
    if (paren.size() == 0)
              
    return nil
            end                
            
    if (n_atom == 0)
              
    return nil 
            end
            
    while (n_atom-=1)>
              result
    <<"." 
            end
            
    while(n_alt>0)  
              result
    <<"|" 
              n_alt
    -=1
            end
            p 
    = paren.pop()
            n_alt 
    = p.n_alt 
            n_atom 
    = p.n_atom 
            n_atom
    +=1
          when 
    '*','+','?':
            
    if (n_atom == 0)
              
    return nil 
            end
            result
    <<
          
    else 
            
    if (n_atom > 1
              n_atom
    -=1 
              result
    <<"."
            end
            result
    <<
            n_atom
    +=1
          end
        end
        
    return nil if paren.size()>0
        
    while ( (n_atom-=1)> 0)
          result
    <<"." 
        end
        
    while(n_alt>0)
          n_alt
    -=1
          result
    <<"|" 
        end
        result
      end
    end

    使用:
     nfa = NFA::compile("a(bb)+a(cdf)*")
     
    assert nfa.test?("abba")
     
    assert nfa.test?("abbbba")
     
    assert !nfa.test?("a"
     
    assert !nfa.test?("aa"
     
    assert nfa.test?("abbacdf")
     
    assert nfa.test?("abbbbacdfcdf")
     
    assert !nfa.test?("bbbbacdfcdf")
     
    assert !nfa.test?("abbbacdfcdf")
     
    assert !nfa.test?("abbbbacdfdf")
     
    assert !nfa.test?("abbbbacdfdfg")



    評論

    # re: 用Ruby寫了個NFA  回復  更多評論   

    2008-02-26 08:57 by left
    好像ruby 的代碼 有點難看
    主站蜘蛛池模板: 最新69国产成人精品免费视频动漫 | 97se亚洲国产综合自在线| 免费一级不卡毛片| 亚洲av无码一区二区三区乱子伦 | 亚洲黄色高清视频| 99久久综合精品免费| 久久久久亚洲AV无码专区首JN | 一级白嫩美女毛片免费| 亚洲视频在线一区二区| 99久久成人国产精品免费| 亚洲av无码乱码国产精品| 99热免费在线观看| 亚洲激情黄色小说| 四虎成人免费影院网址| 国产亚洲精品免费| 亚洲午夜未满十八勿入网站2| 一级毛片成人免费看免费不卡 | 亚洲精品中文字幕乱码影院| 日韩免费一区二区三区在线播放| 亚洲成a∧人片在线观看无码| 亚洲精品国产自在久久| 伊人免费在线观看| 亚洲欧洲国产精品久久| 免费鲁丝片一级观看| 久久高潮一级毛片免费| 亚洲精品偷拍无码不卡av| 国产在线a不卡免费视频| 老司机精品免费视频| 亚洲人色大成年网站在线观看| 国产精品久免费的黄网站| a级毛片毛片免费观看久潮喷| 亚洲最大视频网站| 午夜国产大片免费观看| 男人的天堂网免费网站| 亚洲成在人线aⅴ免费毛片| 亚洲人成无码网站| 噼里啪啦电影在线观看免费高清 | 亚洲国产精品成人AV无码久久综合影院| 在线免费观看h片| 99久久国产亚洲综合精品| 亚洲乱码日产一区三区|