如何在Prolog解释器中使用差异列表

如何在Prolog解释器中使用差异列表,prolog,difference-lists,Prolog,Difference Lists,当我写下来的时候,我想测试一下我对这些结构的了解。然而,当我尝试比较不同的符号这样简单的事情时,似乎我错了,我不理解差异列表的实际情况 ?- L = [a,b,c|[d,e]]-[d,e], L = [a,b,c]. false % expected true 我在SWI Prolog和SICStus上测试了这个。我验证了符号,因为这是如何在Bratko的人工智能Prolog编程(第210页)中编写的,但显然统一是不可能的。为什么呢?这些符号不具有相同的声明意义吗 我认为您认为Prolog解释

当我写下来的时候,我想测试一下我对这些结构的了解。然而,当我尝试比较不同的符号这样简单的事情时,似乎我错了,我不理解差异列表的实际情况

?- L = [a,b,c|[d,e]]-[d,e], L = [a,b,c].
false % expected true

我在SWI Prolog和SICStus上测试了这个。我验证了符号,因为这是如何在Bratko的人工智能Prolog编程(第210页)中编写的,但显然统一是不可能的。为什么呢?这些符号不具有相同的声明意义吗

我认为您认为Prolog解释器将差异列表视为一种特殊的东西。事实并非如此:Prolog不知道差异列表的概念,也不知道几乎所有的概念,除了一些语法上的糖。他只看到:

L=-( |(a, |(b, |(c, |(d, |(e, []))))), |(d, |(e, [] )))
其中-/2和|/2是函子,a、b、c、d、e和[]是常数

差异列表只是一种编程技术,例如动态编程也是一种技术,编译器无法检测或以不同方式处理动态编程程序。它用于有效地统一表达式中部分未统一的部分

假设您要附加/3两个列表。您可以按如下方式执行此操作:

%append(A,B,C).
append([],L,L).
append([H|T],L,[H|B]) :-
    append(T,L,B).
但这是一个很好的例子:首先需要遍历整个第一个列表。如果该列表包含数千个元素,则需要花费大量时间

现在,您可以定义一个契约,您将为它提供一个append\u diff/3,不仅是列表,而且是一个元组列表,Tail,其中list是对列表开头的引用,Tail是对非统一列表结尾的引用。满足此要求的结构示例为尾尾尾、[a |尾]-Tail、[1,4,2,5 |尾]-Tail

现在,您可以在O1中有效地附加_diff/3,包括:

为什么??因为您统一了第一个列表和第二个列表的未统一尾部。现在,第二个列表的未统一尾部成为最终列表的尾部。例如:

append_diff([a|T1]-T1,[1,4,2,5|T2]-T2,L).
如果你调用谓词,如上所述,T1将与[1,4,2,5 | T2]统一,因此现在第一个列表折叠为[a |[1,4,2,5 | T2]]或更短的[a,1,4,2,5 | T2],因为我们也有一个对T2的引用,我们可以在Prolog中返回没有返回任何东西[a,1,4,2,5 | T2]-T2:一个带开放尾T2的新差异列表。但这仅仅是因为你自己给了-一个特殊的含义:对于Prolog-是简单的-,它不是负的,它不计算差,等等。Prolog不给函子附加语义。如果您使用+而不是-,则不会有丝毫区别


回到你的问题:你只需在序言中声明L=-[a,b,c,d,e],[d,e],然后声明L=[a,b,c]。现在很明显,这两个表达不能统一。所以Prolog说false。

我认为您认为Prolog解释器将差异列表视为一种特殊的东西。事实并非如此:Prolog不知道差异列表的概念,也不知道几乎所有的概念,除了一些语法上的糖。他只看到:

L=-( |(a, |(b, |(c, |(d, |(e, []))))), |(d, |(e, [] )))
其中-/2和|/2是函子,a、b、c、d、e和[]是常数

差异列表只是一种编程技术,例如动态编程也是一种技术,编译器无法检测或以不同方式处理动态编程程序。它用于有效地统一表达式中部分未统一的部分

假设您要附加/3两个列表。您可以按如下方式执行此操作:

%append(A,B,C).
append([],L,L).
append([H|T],L,[H|B]) :-
    append(T,L,B).
但这是一个很好的例子:首先需要遍历整个第一个列表。如果该列表包含数千个元素,则需要花费大量时间

现在,您可以定义一个契约,您将为它提供一个append\u diff/3,不仅是列表,而且是一个元组列表,Tail,其中list是对列表开头的引用,Tail是对非统一列表结尾的引用。满足此要求的结构示例为尾尾尾、[a |尾]-Tail、[1,4,2,5 |尾]-Tail

现在,您可以在O1中有效地附加_diff/3,包括:

为什么??因为您统一了第一个列表和第二个列表的未统一尾部。现在,第二个列表的未统一尾部成为最终列表的尾部。例如:

append_diff([a|T1]-T1,[1,4,2,5|T2]-T2,L).
如果你调用谓词,如上所述,T1将与[1,4,2,5 | T2]统一,因此现在第一个列表折叠为[a |[1,4,2,5 | T2]]或更短的[a,1,4,2,5 | T2],因为我们也有一个对T2的引用,我们可以在Prolog中返回没有返回任何东西[a,1,4,2,5 | T2]-T2:一个带开放尾T2的新差异列表。但这仅仅是因为你自己给了-一个特殊的含义:对于Prolog-是简单的-,它不是负的,它不计算差,等等。Prolog不给函子附加语义。如果您使用+而不是-,则不会有丝毫区别

回到你的问题:你只需在序言中声明L=-[a,b,c,d,e],[d,e],然后声明L=[a,b,c]。不
很明显,这两个表达式不能统一。因此,序言说错误。

阅读该部分的关键是“表示”。如果你看这本书,当这些例子可以与顶级的解释器一起使用时,它们的前缀是?-。在该部分中,其读取使得列表[a,b,c]可以例如由[a,b,c][]或[a,b,c,d,e]-[d,e]或[a,b,c,d,e | T]-[d,e | T]或[a,b,c | T]-T表示,其中T是任何列表。因此,这不是一个与顶层一起运行的示例,而是代表您应该思考的内容的示例,因此在顶层将其用作输入时会出现错误。L不能同时是[a,b,c |[d,e]-[d,e]和[a,b,c]。这是两种不同的结构,我现在看到了,但正如评论中所阐明的,这可能就是为什么我没有看到差异列表的实际好处。如果你想使用串联列表,你总是需要Willem Van Onsem建议的finalize步骤。这是真的。差异列表有一个漏洞,如果你需要一个最终的、基本的结果,最终必须填补这个漏洞。有趣的是:阅读该部分的关键是“代表”这个词。如果你看这本书,当这些例子可以与顶级的解释器一起使用时,它们的前缀是?-。在该部分中,其读取使得列表[a,b,c]可以例如由[a,b,c][]或[a,b,c,d,e]-[d,e]或[a,b,c,d,e | T]-[d,e | T]或[a,b,c | T]-T表示,其中T是任何列表。因此,这不是一个与顶层一起运行的示例,而是代表您应该思考的内容的示例,因此在顶层将其用作输入时会出现错误。L不能同时是[a,b,c |[d,e]-[d,e]和[a,b,c]。这是两种不同的结构,我现在看到了,但正如评论中所阐明的,这可能就是为什么我没有看到差异列表的实际好处。如果你想使用串联列表,你总是需要Willem Van Onsem建议的finalize步骤。这是真的。差异列表中有一个漏洞,如果您需要最终的基本结果,那么最终必须填补这个漏洞。有趣的是:我现在理解了它们为什么不统一,但我不理解append_diff/3在实践中有何用处。我的意思是,如果你是这样附加的,结果将是[a,1,4,2,5 | T2]-T2。您仍然没有所需的值,即[a,1,4,2,5],对吗?@BramVanroy:您可以定义一个谓词finalizeA-[],a.来终结差异列表。如果然后调用finalize[a,1,4,2,5 | T2]-T2,则返回结果。结果将是[a,1,4,2,5]。但正如前面所说,这完全取决于您自己如何解释数据。您可以将[a,1,4,2,5 | T2]-T2解释为与[a,1,4,2,5]等价的列表,但Prolog不会这样解释结构,因此finalize/2步骤需要有一个可行的串联列表,对吗?以下是关于其他问题的评论。例如,我们想附加[a,b]和[c,d]。建议的解决方案:conc|d[a,b | T]-T[d,e | T1]-T1,L。然而,结果将是L=[a,b,c,d | T1]-T1,但作为程序员,我需要[a,b,c,d],简单明了。我有一种感觉,我没有抓住要点,但我看不到实际的好处,也看不到如何在实践中使用差异列表。@BramVanroy:你可以使用conc_d[a,b | t]-t[d,e | T1]-T1,Temp,finalizeTemp,L,然后使用进程L,即[a,b,c,d]。关键在于效率:算法将在O1而不是On中运行。但正如前面所说,Prolog并不解释任何结构:数据的语义只存在于程序员的优雅之中,而不是函子本身。注意,您不必使用finalize/2:您只需编写一个程序,其中所有子句都将diff列表作为参数。您可以像处理简单列表一样,对差异列表进行分析和处理。我现在理解了它们为什么不统一,但我不理解append_diff/3在实践中如何有用。我的意思是,如果你是这样附加的,结果将是[a,1,4,2,5 | T2]-T2。您仍然没有所需的值,即[a,1,4,2,5],对吗?@BramVanroy:您可以定义一个谓词finalizeA-[],a.来终结差异列表。如果然后调用finalize[a,1,4,2,5 | T2]-T2,则返回结果。结果将是[a,1,4,2,5]。但正如前面所说,这完全取决于您自己如何解释数据。您可以将[a,1,4,2,5 | T2]-T2解释为与[a,1,4,2,5]等价的列表,但Prolog不会这样解释结构,因此finalize/2步骤需要有一个可行的串联列表,对吗?以下是关于其他问题的评论。例如,我们想附加[a,b]和[c,d]。建议的解决方案:conc|d[a,b | T]-T[d,e | T1]-T1,L。然而,结果将是L=[a,b,c,d | T1]-T1,但作为程序员,我需要[a,b,c,d],简单明了。我有一种感觉,我没有抓住要点,但我看不到实际的好处,也看不到如何在实践中使用差异列表。@BramVanroy:你可以使用conc_d[a,b | t]-t[d,e | T1]-T1,Temp,finalizeTemp,L,然后使用进程L,即[a,b,c,d]。关键在于效率:算法 m将在O1中运行,而不是在On上运行。但正如前面所说,Prolog并不解释任何结构:数据的语义只存在于程序员的优雅之中,而不是函子本身。注意,您不必使用finalize/2:您只需编写一个程序,其中所有子句都将diff列表作为参数。您可以像处理简单列表一样,对差异列表进行分析和处理。