【问题标题】:Regular Expression Matching Prolog正则表达式匹配 Prolog
【发布时间】:2012-09-25 02:27:15
【问题描述】:

我正在尝试进行正则表达式匹配。我已经写出了所有功能,但它们没有按应有的方式工作。据我所知,当我尝试比较列表时出现问题。
例如,“re_contains(a,a)”。给出 true (显然),就像 "re_contains(union(a,b),a)" 一样。

但是一旦我把它列成一个列表,它就失败了。 “重新包含(序列(a,b),[a,b])。”返回假。 Append 应该通过所有可能的组合来找到匹配项,但是这些函数都不能正常工作。这让我觉得也许我错过了一个基本案例。但我认为“re_contains(X, L) :- X == L。”应该照顾它。我一定是在看这里重要的事情。

这是我的代码:

re_contains(empty, []).

re_contains(X, L) :-
    X == L.

re_contains(seq(X, Y), L) :-
    append(L1, L2, L),
    re_contains(X, L1),
    re_contains(Y, L2). 

re_contains(union(X, _), L) :-
    re_contains(X, L).

re_contains(union(_, Y), L) :- 
    re_contains(Y, L).  

re_contains(kleene(X), L) :- 
    append([Car|L1], L2, L),
    re_contains(X, [Car|L1]),
    re_contains(kleene(X), L2).

re_contains(kleene(_),[]).

【问题讨论】:

    标签: regex prolog dcg failure-slice


    【解决方案1】:

    有几个问题。以下是最明显的:

    打字。 您的谓词 re_contains/2 需要一个列表作为第二个参数。 re_contains(a,a). 的成功与其说是有意为之,不如说是巧合。

    单调性。另一个问题是re_contains([a],[a]) 成功,但re_contains([X],[a]) 失败。或者,从另一个角度来看:re_contains([X],[a]) 失败,但X = a, re_contains([X],[a]) 成功。通过添加目标X = a,我们专门化了查询,因此最初失败的查询应该再次失败。

    身份测试 (==/2) 应替换为相等 (=/2) 确保我们有一些列表。所以:

    re_contains(X, L) :- % X == L。 X = L, 附加(X,_,_)。

    注意,这里使用append/3 只是为了确保 X 是一个列表 - 不使用实际的附加功能。

    效率。 第三个问题涉及您表示串联的实际方式。让我们看看以下规则:

    re_contains(seq(X, Y), L) :- 附加(L1,L2,L), re_contains(X, L1), 重新包含(Y,L2)。

    现在,假设我们这里有一个长度为N 的列表。目标append(L1, L2, L) 可能有多少个答案?其实N + 1。而这一点,不管所涉及的实际值如何。现在考虑:

    ?- 长度(L,1000000), 时间(re_contains(seq([a],[]),[b|L]))。
    % 2,000,005 次推理,0.886 CPU 在 0.890 秒内(100% CPU,2258604 唇)
    错误的。
    

    re_contains/2 需要的时间与列表的长度成正比。但只要看看第一个元素就足以意识到这是不可能的。

    所以后面的问题是append/3的使用。 Prolog 有一个简单的经验法则:如果您使用太多 append/3,请考虑使用 s — 定从句语法。请查看标签以获取更多详细信息 - 并查阅介绍性 Prolog 文本。为了让您开始,这里是您定义的一个子集:

    re_contains(RE, L) :- 短语(重新(RE),L)。 重新([])-> []。 重新([E])-> [E]。 重新(序列(X,Y))--> 重新(X), 重新(Y)。

    不再调查整个列表:

    ?-长度(L,1000000),时间(短语(re(seq([a],[])),[b|L]))。 % 6 次推理,0.000 秒内 0.000 CPU(88% CPU,127313 唇) 错误的。

    顺便说一句,here 是一个完整的定义。

    终止/非终止。 与效率相关的是终止的属性。理想情况下,如果可以有限地表示一组解决方案,则查询将终止。也就是说,通过有限数量的答案。好的,这就是我们正在努力的理想。 Prolog 的简单但非常有效的执行算法有时会循环,当可能有有限数量的答案时。理解不终止的原因有时非常棘手。通常的调试策略——比如使用调试器进行跟踪或单步调试——不起作用,因为它们向您展示了太多细节。幸运的是,有一种更好的技术:

    我不会再看你的整个程序,而只会看它的一小部分。这个片段是一个failure slice(更多细节参见链接)。它要小得多,但说明了很多关于原始程序的信息——前提是它是一个纯粹的、单调的程序:

    如果失败切片没有终止,则原始程序不会终止。

    因此,如果我们发现这样一个失败片段,我们可以立即对整个程序做出结论。甚至没有阅读其余部分!

    这是一个有趣的失败片段:

    ... re_contains(X, L) :- false, X = L re_contains(seq(X, Y), L) :- 追加(L1,L2,L),假, re_contains(X, L1), re_contains(Y, L2)re_contains(union(X, _), L) :- false, re_contains(X, L)。 ...

    现在考虑目标re_contains(seq([],[]),L). 理想情况下,应该只有一个答案L = [] 并且目标应该终止。但是,它会循环,因为append(L1, L2, L) 不会终止。将此与上面的 DCG 解决方案进行对比,后者以phrase(re(seq([],[])),L) 终止。

    【讨论】:

    • 谢谢。我对 Prolog 语法完全不熟悉。我想我试图做更多的 C# if 语句之类的事情。
    【解决方案2】:

    append/3 将拆分 LL1L2 都将成为列表。

    我会尝试将re_contains(X, L) :- X == L. 替换为re_contains(X, [X]).

    修改后re_contains(a,a).会失效。

    您以不同的方式表示序列,而您的匹配器并没有同时提供这两种方式。实际上,“有效”的唯一情况是不是序列。

    【讨论】:

    • re_contains 确实适用于序列。参考文献re_contains([a],[a]).
    • 当然,但是 OP 不使用列表来表示正则表达式。我建议删除这种不匹配。
    • 存在re_contains(X, L) :- X == L. 的事实表明列表是有意的。
    • @chac 这实际上也很有效。我认为我的问题是我只是假设 Prolog 会知道它是一个列表。我的愚蠢错误。非常感谢你们!
    猜你喜欢
    • 1970-01-01
    • 2019-03-30
    • 2011-05-01
    • 1970-01-01
    相关资源
    最近更新 更多