Parsing 解析器DCG不具有确定性是否合适?

Parsing 解析器DCG不具有确定性是否合适?,parsing,prolog,logic,dcg,deterministic,Parsing,Prolog,Logic,Dcg,Deterministic,我正在为查询引擎编写一个解析器。我的解析器DCGquery不是确定性的 我将以关系方式使用解析器,检查和合成查询 解析器DCG不具有确定性是否合适 代码: 如果我希望能够同时使用query/2,它需要这样做吗 ?- phrase(query, [q,u,e,r,y]). true; false. 或者我应该能够获得 ?- phrase(query, [q,u,e,r,y]). true. 尽管如此,考虑到第一个片段需要我这样使用它 ?- bagof(X, phrase(query, [q,u

我正在为查询引擎编写一个解析器。我的解析器DCG
query
不是确定性的

我将以关系方式使用解析器,检查和合成查询

解析器DCG不具有确定性是否合适

代码: 如果我希望能够同时使用query/2,它需要这样做吗

?- phrase(query, [q,u,e,r,y]).
true;
false.
或者我应该能够获得

?- phrase(query, [q,u,e,r,y]).
true.
尽管如此,考虑到第一个片段需要我这样使用它

?- bagof(X, phrase(query, [q,u,e,r,y]), [true]).
true.

当用它来检查公式时?

首先要问你自己的问题是,你的语法是确定性的,还是用语法的术语。这不是问你的DCG是否确定,而是问语法是否明确。这可以用基本的解析概念来回答,不需要使用DCG来回答这个问题。换句话说,只有一种方法可以解析有效的输入。这方面的标准书籍是“编译器:原理、技术和工具”()

现在您实际上在询问解析的三种不同用途

  • 识别器
  • 解析器
  • 发电机
  • 如果你的语法是明确的,那么

  • 对于识别器,只能对可解析的有效输入为true,对无效输入为false
  • 对于解析器,它应该是确定性的,因为只有一种方法可以解析输入。解析器和识别器之间的区别在于识别器只返回true或false,而解析器将返回更多的内容,通常是抽象语法树
  • 对于生成器,它应该是半确定性的,以便能够生成多个结果
  • 所有这些都可以用一个来完成吗,DCG,是的。这三种不同的方式取决于您如何使用DCG的输入和输出


    下面是一个语法非常简单的示例

    语法只是一个中缀二进制表达式,包含一个运算符和两个可能的操作数。运算符为(+),操作数为(1)或(2)

    语法是明确的,只有4个唯一的有效字符串

    识别器是确定性的,只返回true或false。
    解析器是确定性的,并返回唯一的AST。
    生成器是半确定性的,返回所有4个有效的唯一字符串

    测试用例的示例运行

    ?- run_tests.
    % PL-Unit: expr ........... done
    % All 11 tests passed
    true.
    

    对Daniel的评论进行一点扩展

    正如丹尼尔所指出的

    1 + 2 + 3 
    
    可以解析为

    (1 + 2) + 3 
    

    因此,
    1+2+3
    就是一个例子,正如您所说,
    是由递归DCG指定的,正如我所指出的,解决问题的一个常见方法是使用括号来启动新上下文。开始一个新的上下文的意思是,它就像让一个新的上下文重新开始一样。如果要创建AST,只需将新上下文(括号之间的项)作为新子树放在当前节点上

    对于,这也很有帮助,但要注意操作符的左关联性和右关联性。看

    e、 g

    +
    是左关联的

    ?- write_canonical(1+2+3).
    +(+(1,2),3)
    true.
    
    ?- write_canonical(2^3^4).
    ^(2,^(3,4))
    true.
    
    ^
    是右关联的

    ?- write_canonical(1+2+3).
    +(+(1,2),3)
    true.
    
    ?- write_canonical(2^3^4).
    ^(2,^(3,4))
    true.
    
    i、 e


    添加这些信息的目的是警告你,语法设计充满了隐藏的陷阱,如果你没有一门严格的课程,并且做了一些,你可以很容易地创建一个看起来很好、效果很好的语法,然后几年后发现有严重的问题。虽然Python在AFAIK中并不含糊,但它确实存在语法问题,在创建Python3时,已经解决了很多问题。因此,Python3与Python2()不向后兼容。是的,他们已经做了一些更改和库,使Python 2代码更容易与Python 3一起使用,但关键是语法在设计时可以使用更多的分析。

    代码应该是非确定性的唯一原因是您的问题有多个答案。在这种情况下,您当然希望查询具有多个解决方案。然而,即使如此,如果可能的话,您也希望它在最后一个解决方案之后留下一个选择点

    我的意思是:

    “两个数字中较小的是什么?”

    这是不错的代码,但我认为它仍然是垃圾。这就是为什么,对于两个数字中较小的一个,您会使用以下内容

    类似地,假设你想问,“1到10之间的偶数是多少”;您可以编写查询:

    ?- between(1, 10, X), X rem 2 =:= 0.
    X = 2 ;
    X = 4 ;
    X = 6 ;
    X = 8 ;
    X = 10.
    
    。。。这很好,但是如果你要的是3的倍数,你会得到:

    ?- between(1, 10, X), X rem 3 =:= 0.
    X = 3 ;
    X = 6 ;
    X = 9 ;
    false. % crap...
    
    “低挂果实”是指作为程序员,您会发现不可能存在非确定性,但由于某种原因,您的Prolog无法从您编写的代码中推断出非确定性。在大多数情况下,你可以做点什么

    关于你的实际问题。如果可以,请编写代码,以便只有当您要问的问题有多个答案时,才会出现非确定性。当您使用DCG进行解析和生成时,这有时意味着您将得到两条代码路径。这感觉很笨拙,但它更容易书写、阅读、理解,而且可能更有效率。作为警告,请看一看。我不能确定这一点,但OP遇到的问题几乎肯定是由不必要的非决定论造成的。对于较大的输入,可能会出现以下情况:大量的选择点被留下,大量的内存无法回收,大量的处理时间用于簿记,巨大的解决方案树被遍历,结果(如预期)没有得到任何解决方案。。。。你明白了

    对于我的意思的例子,你可以看看。注意几件事:

    • 文档非常明确地说明了什么是确定性的,什么不是确定性的,以及非确定性对客户机代码的用处
    • 在必要时使用削减,以消除无用的选择点
    • 代码的实现
      ?- min_a(1, 2, Min).
      Min = 1.
      
      ?- min_a(2, 1, Min).
      Min = 1 ; % crap...
      false.
      
      ?- min_a(2, 1, 2).
      false.
      
      ?- min_a(2, 1, 1).
      true ; % crap...
      false.
      
      ?- between(1, 10, X), X rem 2 =:= 0.
      X = 2 ;
      X = 4 ;
      X = 6 ;
      X = 8 ;
      X = 10.
      
      ?- between(1, 10, X), X rem 3 =:= 0.
      X = 3 ;
      X = 6 ;
      X = 9 ;
      false. % crap...