【问题标题】:DCG: Assigning remainder to a variable within DCG {} codeDCG:将余数分配给 DCG {} 代码中的变量
【发布时间】:2013-09-10 12:37:38
【问题描述】:

这个答案:Very basic dcg prolog syntax 帮助了我一点,但 [X] 只得到下一个字符,我想要整个辣酱玉米饼馅,继续阅读!

我正在使用 GNU Prolog 编写命令选项解析器,但我被困在 DCG 点上。例如,我有一个查找“foo --as=json”的语法规则,但我无法弄清楚如何获得“任何东西”的结果,代码:

as_opt --> "--as=", anything, { c( as_opt )}, !.
anything --> [], {c(anything_match)}.

而 gprolog 的扩展是:

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B.

anything(A, B) :-
        c(anything_match), !,
        A = B.

“c()”谓词很简单,仅用于跟踪使用 format() 到标准输出执行的规则,因此我可以看到它运行时发生了什么。如果我手动编写代码,我会这样做:

%% for completeness here!
c(Msg) :- format("Processed ~w~n", [Msg]).

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B,
        { g_assign( gvValue, B )}. %% just for example

回到原来的 DCG:

as_opt --> "--as=", anything, { c( as_opt ), gassign( gvValue, ??? )}, !.

那么“???”是。有没有可能……应该有。我将重新阅读 gprolog 规则,了解它如何再次扩展 DCG 规则,以防我自己(捂脸)但同时欢迎任何帮助。

谢谢, 肖恩。

【问题讨论】:

    标签: prolog gnu dcg


    【解决方案1】:

    有一个比 mat 描述的更好的解决方案,它更快并且避免了虚假的选择点,但它需要支持 call//1 内置非终端,如在例如SWI-Prolog、GNU Prolog 和其他 Prolog 编译器。也在 Logtalk 上。考虑:

    as_opt(Option) --> "--as=", list(Option).
    
    list([L|Ls]) --> [L], list(Ls).
    list([]) --> [].
    
    as_opt2(Option) --> "--as=", call(rest(Option)).
    
    rest(Rest, Rest, _).
    

    使用 SWI-Prolog 更好地说明差异:

    ?- phrase(as_opt(Option), "--as=json").
    Option = [106, 115, 111, 110] ;
    false.
    
    ?- phrase(as_opt2(Option), "--as=json").
    Option = [106, 115, 111, 110].
    
    ?- time(phrase(as_opt(Option), "--as=json")).
    % 9 inferences, 0.000 CPU in 0.000 seconds (57% CPU, 562500 Lips)
    Option = [106, 115, 111, 110] ;
    % 2 inferences, 0.000 CPU in 0.000 seconds (63% CPU, 133333 Lips)
    false.
    
    ?- time(phrase(as_opt2(Option), "--as=json")).
    % 6 inferences, 0.000 CPU in 0.000 seconds (51% CPU, 285714 Lips)
    Option = [106, 115, 111, 110].
    

    虚假选择点来自list//1 非终结符的定义。性能差异在于,虽然call//1 允许我们简单地访问隐式列表参数,但list//1 非终端正在逐个元素地执行该隐式参数的列表复制。因此,list//1 版本的性能在--as= 前缀后面的字符上是线性的,而call//1 版本的性能是恒定的,与前缀后面的字符数无关:

    ?- time(phrase(as_opt(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
    % 54 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 2700000 Lips)
    Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...] ;
    % 4 inferences, 0.000 CPU in 0.000 seconds (69% CPU, 137931 Lips)
    false.
    
    ?- time(phrase(as_opt2(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
    % 6 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 333333 Lips)
    Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...].
    

    【讨论】:

    • 哇哦!保罗,这感觉是一个非常好的答案,但就理解而言,它直接超出了我的理解!我只使用 Prolog 一到三个月。我可以理解你的意思,但选择点,这确实是一个好点,我仍然需要完全了解这一点。现实情况是,我的 DCG 将执行一次以解析可能不超过四到五个命令行参数;它只需要工作。不过谢谢。 :)
    • call//1 非终端调用被转换为call/3 内置谓词调用,其中第一个参数(rest(Rest) 闭包)是共享的,另外两个参数(在 @ 987654334@ call) 是 DCG 规则的两个隐含参数。 rest/3 谓词的定义只是将闭包参数与第一个(也称为“输入”)隐式参数统一起来。还有一个替代解决方案使用 call//1 内置非终端和 lambda 表达式作为不需要辅助谓词(例如 rest/3)的参数,但这是一个讨厌的技巧,我不会发布它。
    • 保罗,我们不应该在现实生活中相遇。如果你的大脑和我的反大脑合作,爆炸可能是致命的……谢谢! :)
    【解决方案2】:

    我认为你不应该使用全局变量。相反,向 DCG 子句添加参数,以取​​回值:

    anything([C|Cs]) --> [C], anything(Cs).
    anything([]) --> [].
    

    否则,在规则成功后,剩余部分可用作短语/3 的最后一个参数,“消耗”标题匹配。

    【讨论】:

    • 我对 Prolog 比较陌生。我想我应该阅读短语/3 的手册,因为我在命令行参数的迭代循环中“手动”调用启动规则。我使用全局变量是因为我正在用 gprolog 编写一个较大的应用程序。解析命令行选项只是我需要做的事情之一。它使代码的其他部分变得容易,并且我的全局变量只有在命令行模块中设置后才会被严格读取。不过,我完全理解您的保留意见。
    【解决方案3】:

    您要的是最简单的 DCG 非终结符之一,它可以描述任何列表:

    list --> [].
    list --> [_], list.
    

    要实际访问正在描述的列表,您需要引入一个参数:

    list([])    --> [].
    list([L|Ls) --> [L], list(Ls)
    

    你可以这样使用它:

    as_opt(Option) --> "--as=", list(Option).
    

    【讨论】:

    • 垫子,很好的答案。我想作为 Prolog 和 DCG 规则的菜鸟,我还没有体会到其中的细微差别!我会处理它,毫无疑问,追踪它会帮助我更多地了解它。谢谢。一个很好的简洁答案。
    • 无赖。应该已经尝试了代码,太兴奋了!它对我还不起作用。它输出“[]”作为选项的值。我会更加努力...我想很快就会回来...
    • mat,我认为规则的顺序是错误的。如果我把空列表放在最后,事情看起来好多了!您认为这是您的疏忽吗?
    • 好的。我现在已经接受了这个答案,因为一旦我交换了规则,它就对我有用。我可以使用它,也可以更多地了解 Prolog。谢谢垫子。
    • 如果你要解析的字符串Ls由“--as=”组成,那么?- phrase(as_opt(O), Ls)不能用O=[]成功。始终使用phrase/2 接口连接 DCG。在某些情况下,交换规则是有意义的(以获得最长的输入匹配),但我认为这不是其中之一,因为您可能有多种选择,而且我除了您的主界面谓词看起来与options([O|Os]) --> as_opt(O), " ", options(Os). 非常相似,其中分隔空白绝对不应该是正在解析的选项的一部分(尽管您最初要求这样做)。
    猜你喜欢
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多