我想写一个Prolog谓词,它接受一个复合词作为其参数,并输出这个复合词,删除了一些嵌套词。例如,让我们说我有一个复合词:
outer_term(level_one(level_two_a(X), level_two_b(Y)), level_one(level_two_b(Z))).
我想编写一个谓词extract_terms/2
,它将使用这个术语并在没有出现level_two_a/1
的情况下返回它。
extract_terms(Term, ExtractedTerm) :-
*** Prolog Magic ***.
Prolog中是否有内置(或半内置)方式?如果没有,我该怎么做呢?我遇到的一种方法是使用=../2
运算符将Term转换为列表,然后以某种方式使用某些内置谓词(如subtract/3
)来删除我想要的谓词。我遇到的麻烦是使用嵌套术语作为项目的列表来使用它。
我很感激任何想法,谢谢。
答案 0 :(得分:4)
首先,一般准则:
模式匹配表示 的所有内容应通过模式匹配来表达。
你之前问过similar question,虽然它有点简单。不过,让我们先考虑一个更简单的案例:你说一个动词短语的可能实例是:
VP = vp(vp(verb(making), adj(quick), np2(noun(improvements))))
并且您想要提取动词。嗯,最简单的方法是使用模式匹配,或者更常见的是统一,如下所示:
?- VP = vp(vp(verb(making), adj(quick), np2(noun(improvements)))), VP = vp(vp(Verb, _, _)).
这会产生:
Verb = verb(making).
因此,我们通过统一成功地从这样的短语中“提取”了verb(making)
。
现在您要在这个问题中考虑稍微复杂的任务:此时,您可能想知道您是否选择了数据的良好表示。经常使用甚至(=..)/2
的必要性通常表示您的代表性存在问题,因为这可能意味着您已经失去了对数据可能形状的跟踪或控制。
在这个具体案例中,您举例说明:
outer_term(level_one(level_two_a(X), level_two_b(Y)), level_one(level_two_b(Z))).
并且您想删除level_two_a
的出现次数。您现在当然可以开始混淆(=..)/2
,这需要将这些术语转换为列表,然后在这些列表上进行一些推理,以及从列表转换回此类结构的第二次转换。这不是我们想要处理数据的方式。除了其他缺点之外,它还会排除我们期望从关系中获得的更一般的使用模式。
相反,让我们修复数据表示,以便我们可以干净地区分不同的情况。例如,我们不需要“硬编码”我们需要区分仿函数 的情况的参数,让我们区分 explicit :我们希望能够区分,通过模式匹配,从第2级开始第1级。
因此,以下表示表明了自己:
outer_term([level(1, [level(2, X), level(2, Y)]), level(1, [level(2, Z)])]).
这可能需要一些额外的属性,例如a
和b
,并且我将此表示扩展为将这些属性表示为一个简单的练习。一般的想法应该是清楚的:我们已经实现了一个统一的表示,我们可以很容易地用符号来推理。
现在可以很容易地描述这些级别的(可能嵌套的)列表与没有“级别2”元素的级别之间的关系:
without_level_2(Ls0, Ls) :- phrase(no_level_2(Ls0), Ls). no_level_2([]) --> []. no_level_2([L|Ls]) --> no_level_2_(L), no_level_2(Ls). no_level_2_(level(2,_)) --> []. no_level_2_(level(L,Ls0)) --> [level(L,Ls)], { dif(L, 2), without_level_2(Ls0, Ls) }.
有关此形式主义的更多信息,请参阅dcg。
示例查询:
?- outer_term(Ts0), without_level_2(Ts0, Ts).
产量:
Ts = [level(1, []), level(1, [])] .
请注意,要真正从此表示中受益,您需要首先通过使用或生成此类形状的术语来获取它。一旦确保了这一点,您可以方便地坚持模式匹配以区分案例。在这种方法的主要优点中,我们发现便利性,性能和通用性。例如,我们可以使用上面显示的DCG,不仅可以提取,还可以使用生成这种形式的术语:
?- length(Ls0, _), without_level_2(Ls0, Ls). Ls0 = Ls, Ls = [] ; Ls0 = [level(2, _56)], Ls = [] ; Ls0 = Ls, Ls = [level(_130, [])], dif(_130, 2) ; Ls0 = [level(_150, [level(2, _164)])], Ls = [level(_150, [])], dif(_150, 2) .
这是一个真正的关系,可用于所有方向。出于这个原因,我在谓词名称中避免了命令式名称,例如“extract”,“remove”等,因为这些名称始终建议使用特定的方向,而不是正义的谓词的普遍性。