`

用Ruby写个NFA

阅读更多

    今天有点空闲,想想用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)>0 
          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<<c 
      else 
        if (n_atom > 1) 
          n_atom-=1 
          result<<"."
        end
        result<<c 
        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")
 

 

6
2
分享到:
评论
1 楼 ShiningRay 2008-02-26  
拿脚本语言写纯粹算法上的东西当然会慢了

相关推荐

    NFA.rar_NFA.1888COM_NFA1888COM_nfa_nfa java

    NFA,全称“不确定的有限自动机”(Non-deterministic Finite Automaton),是自动机理论中的一个重要概念。本篇主要围绕NFA的概念、特点以及在Java中的实现进行深入探讨。 NFA是一种模型,用于识别和处理形式语言...

    编译原理实验 NFA确定化

    在这个实验中,我们将探讨NFA确定化的概念、方法以及如何通过编程实现这一过程。 NFA(非确定性有限状态自动机)具有以下特点: 1. 每个状态下都有多个可能的转移,对应于输入符号的不同组合。 2. NFA可以有 ε ...

    正则表达式转换为NFA(Regex to NFA).jar

    用JAVA写的一个将正则表达式转换为NFA的代码,基于Thompson算法的思想,递归构建NFA。jar为源码文件。 输出非确定有限自动状态机的有向图。如正则表达式: c(a|b)NFA为:0-c-&gt;1-ep-&gt;2-a-&gt;3-ep-&gt;7 ,0-c-&gt;1-ep-&gt;4-b-&gt;5-...

    用C语言实现NFA到DFA的转换过程

    用C语言实现NFA到DFA的转换过程 NFA (nondeterministic finite-state automata)是不确定性有限状态自动机的简写,NFA的定义为: 一个不确定性有限状态自动机由以下部分所组成: A. 一个有限的输入字符集I B. 一个...

    编译原理Java实现NFA到DFA的等价变换

    Non-Deterministic Finite Automaton(NFA)是一种更一般的自动机,它的状态变迁函数是一个非确定的函数,即给定当前状态和输入符号,下一个状态可能有多个选择。NFA也可以用五元组M=(Q,∑,δ,q0,Z)来表示,但δ是从...

    NFA确定化,NFA转DFA

    1. **NFA**:NFA是一种有限状态自动机,它允许在每个状态下根据当前输入符号有多个可能的转移。这种非确定性使得NFA能够更高效地处理某些正则表达式,但同时也带来了不确定性,即对于同一个输入序列,NFA可能有多种...

    编译原理NFA转DFA实现(python).zip

    3. **Python实现细节**:描述如何用Python代码实现NFA和DFA的数据结构,以及如何定义转换函数。 4. **实验结果**:展示转换前后的状态图,以及对于不同输入字符串的识别情况。 5. **问题与解决方案**:可能遇到的...

    NFA确定化为DFA

    这个过程会构建出一个状态转换表,表中的每个单元格代表一个DFA状态,对应着NFA的一组状态,单元格的每一列表示对某个输入字符的转移。 在Java中实现NFA到DFA的确定化,首先需要定义NFA和DFA的结构。NFA的状态通常...

    NFA--DFAmin

    NFA(非确定有限状态自动机)允许在每个状态下有多个可能的转移,即对于一个输入符号,NFA可以从当前状态转移到多个状态。DFA(确定有限状态自动机)则更严格,每个状态下对于每个输入符号只有一个确定的转移。DFA比...

    NFA到DFA的转化程序

    本主题将深入探讨如何将NFA转换为DFA,并通过一个Java实现的程序来展示这一过程。 首先,NFA(Non-Deterministic Finite Automaton)是一种允许存在多种可能转移路径的自动机。每个状态下,对于同一个输入符号,它...

    NFA转DFA程序代码

    本人用c++写的一个将NFA转换成DFA的程序,希望大家指教。

    NFA转换为DFA(子集构造法)

    本项目以"NFA转换为DFA(子集构造法)"为主题,旨在实现一个将非确定性自动机转化为确定性自动机的程序,算法基于编译原理教科书中的方法,作者陈意云。 首先,我们来理解NFA和DFA的基本概念。NFA是一种可能具有多...

    正则表达式到NFA

    并集通过创建一个新的起始状态,从该状态分别指向两个子NFA的起始状态,并将两个子NFA的终态合并为一个终态;闭包操作在原状态和自身之间添加一条ε转移。 4. **ε-归约**:在NFA构建完成后,可能需要进行ε-归约,...

    正则表达式转NFA实现

    1. **基础构造**:首先,为每个基本字符构造一个NFA状态,这个状态只接受该字符,并且从初始状态有边直接指向这个状态。 2. **组合运算符**:对于正则表达式的组合运算符如“+”(一次或多次)、“*”(零次或多次...

    用Java做编译原理正规式转换成NFA.rar

    本资源“用Java做编译原理正规式转换成NFA.rar”主要关注的是编译原理中的一个重要概念——正规式到非确定性有限自动机(NFA)的转换。下面我们将深入探讨这个主题。 正规式是描述有限语言的数学表达式,常用于定义...

    计算NFA中ε闭包

    **非确定有限自动机(NFA)**是一种理论计算模型,它扩展了确定有限自动机(DFA)的概念,允许在某些情况下从一个状态出发到达多个状态。在NFA中,存在一种特殊的转移类型称为ε-转移,这种转移可以在不消耗任何输入符号...

    编译原理中正规式转化为nfa

    在编译原理中,正规式(Regular Expression)与非确定性有限自动机(Non-Deterministic Finite Automaton,简称NFA)是两个重要的概念。正规式主要用于描述简单的字符串模式,而NFA则是一种数学模型,用于识别正规集...

    NFA源代码 识别字符

    编译原理的实验 NFA识别 java编写

    编译原理\NFA的构造

    #include #include class DFA; //声明DFA class NFA { char K[100]; //NFA的状态集合K ... // 创建一个NFA void PrintNFA();// 输出一个NFA friend void NFA_to_DFA(NFA &,DFA &);//实现NFA到DFA的转换 };

    将正规式转换成NFA

    在C语言实现中,NFA通常用结构体表示状态,包括状态编号、转移函数(一个二维数组,每个元素指向下一个状态)和是否为接受状态的布尔标志。`N_DFA.c`可能是这样一个实现,包含了创建、添加转移和执行NFA匹配的函数。...

Global site tag (gtag.js) - Google Analytics