从OCaml中的语法中删除无法访问的规则

从OCaml中的语法中删除无法访问的规则,ocaml,grammar,Ocaml,Grammar,我是Ocaml新手,为了完成家庭作业,我必须编写一个函数filter\u reachable 它接受一个语法并返回一个简化的语法,其中删除了不可访问的规则。我只允许使用渗透和列表模块,而不允许使用其他模块 我的想法是首先列出所有可访问的非终端。如果规则的非终端部分在所有可访问的非终端列表中,则该规则是可访问的。然后将所有可访问的规则放入一个新列表中,并与开始符号配对以创建简化语法 我的解决方案如下。但是它不起作用,我不明白为什么。谁能帮我修一下吗 let rec member x s= matc

我是Ocaml新手,为了完成家庭作业,我必须编写一个函数filter\u reachable 它接受一个语法并返回一个简化的语法,其中删除了不可访问的规则。我只允许使用渗透和列表模块,而不允许使用其他模块

我的想法是首先列出所有可访问的非终端。如果规则的非终端部分在所有可访问的非终端列表中,则该规则是可访问的。然后将所有可访问的规则放入一个新列表中,并与开始符号配对以创建简化语法

我的解决方案如下。但是它不起作用,我不明白为什么。谁能帮我修一下吗

let rec member x s=
match s with
[]->false
| y::ys-> (x = y) || member x ys
     (*the type of a symbol*)

type ('nonterminal, 'terminal) symbol =
  | N of 'nonterminal
  | T of 'terminal


let rec get_nont sl=
match sl with
|[]->[]
|h::t->match h with
      |N x-> x::get_nont t
      |T y-> get_nont t

let rec get_rea_nont (n,r) =
n::(match r with
|[]->[]
|h::t->match h with
   | a,b -> if a=n then (get_nont b)@(get_rea_nont (n,t))
            else get_rea_nont(n,t)
   | _-> [])

let rec fil (st,rl)=
let x = get_rea_nont(st,rl) in
(match rl with
|[]-> []
|h::t-> match h with
        |a,b -> if (member a x) then h::fil(st,t)
                else fil(st,t)
        |_->[]
|_->[]
)

let rec filter(st,rl)=
(st,fil(st,rl))

想象一下对
fil(st,rl)
的第二个递归调用。在这个调用中,
rl
只有一条规则。您的代码将尝试通过查看这条规则来发现是否可以访问该规则的非终结符。除非非终结符是开始符号,否则这不会起作用

一般来说,我会说你必须多带一些背景知识。我不想透露太多,但这基本上是一个经典的有向图遍历问题。每个语法规则都是图中的一个节点。边从规则R转到定义R的RHS中出现的非终结符的其他规则。这种著名的图遍历算法通常适用于“已访问”节点列表。您需要这个,因为图形可以有循环

下面是一个带有循环的语法:

A ::= B | B '+' A
B :: = 'x' | 'y' | '(' A ')'

关于代码的一些备注:

  • 您可以使用

  • get\u nont
    中的模式匹配可以组合:

    let rec get_nont sl = match sl with
        | [] -> []
        | N x :: tl -> x :: get_nont tl
        | T _ :: tl -> get_nont tl;;
    
  • 在函数式编程中,curry用于定义具有多个参数的函数。请参阅“Curried函数”一节

  • 使用模式匹配的功能,例如为
    get\u rea\u nont
    演示:

    let rec get_rea_nont_old n r = n :: (match r with
        | []->[]
        | (a, b) :: t when a = n -> (get_nont b) @ (get_rea_nont_old n t)
        | _ :: t -> get_rea_nont_old n t
    );;
    
  • 尝试模块化您的代码,例如,为了
    get\u rea\u nont

    let rec get_rea_nont_old n r = n :: (match r with
        | []->[]
        | (a, b) :: t when a = n -> (get_nont b) @ (get_rea_nont_old n t)
        | _ :: t -> get_rea_nont_old n t
    );;
    
    • 第一个滤芯等于
      n
      (请参阅)
    • 对于过滤后的元素,应用函数
      get\u nont
      (请参阅)
    • 考虑得到预期的结果
    因此

    let get_rea_nont n r =
        let filtered = List.filter (fun (a, _) -> a = n) r in
        let nonterminals = List.map (fun (_, b) -> get_nont b) filtered in
        n :: (List.flatten nonterminals);;
    

  • 你打算用什么类型来表示语法?我只看到单个符号(终端或非终端)的定义。作为旁注,您的函数
    成员
    可在列表模块的name List.mem下找到。这绝对值得您熟悉列表模块的功能,特别是因为您可以使用它们!语法中使用的符号。它可以是非终结符符号或终结符符号;每种符号都有一个值,其类型是任意的。符号类型已发布,右侧是符号列表。它对应于单个语法规则的右侧。右手边可以是空的。规则是一对,由(1)非终结值(语法规则的左侧)和(2)右侧组成。语法是一对,由开始符号和规则列表组成。开始符号是一个非终结值。那么,名为
    rl
    的参数表示规则列表,一对(st,rl)表示语法。