有关prolog回溯以寻找其他解决方案的问题

时间:2014-07-31 02:00:36

标签: recursion prolog backtracking

我是Prolog的初学者。

我所拥有的是一个函数遍历列表,并在满足条件时返回true。

例如,check_version检查包版本是否满足条件(例如,版本满足条件,例如大于或小于特定版本),check_all检查采用一个版本和条件列表逐个检查。

package('python', '2.6.5').
package('python', '2.5.4').
package('python', '1.5.2').
package('python', '3.1.0').

check_version(Pac, Ver, Cmp, V):-
   package(Pac, V),
   cmp_version(V, Ver, Cmp).

check_all( Pac, [], [], V):-
   package(Pac, V).
check_all(Pac, [Ver], [Cmp], V):-
   check_version(Pac, Ver, Cmp, V).
check_all(Pac, [Ver|VerS], [Cmp|CmpS], V):-
   check_version(Pac, Ver, Cmp, V),
   check_all(Pac, VerS, CmpS, V).

我遇到的问题是在尝试寻找其他解决方案时,它给了我重复的解决方案。

我明白了:

check_all('python', ['3.0','2.4'], [lt,ge], V).

V = '2.6.5' ;
V = '2.6.5' ;
V = '2.5.4' ;
V = '2.5.4' .

预期:

check_all('python', ['3.0','2.4'], [lt,ge], V).
V = '2.6.5' ;
V = '2.5.4' .

我使用跟踪来跟踪它,并且我发现了问题,当它试图找到另一个解决方案时它会跟踪并返回失败,直到找到正确的解决方案。就像上面的例子一样,显然,它首先会返回V ='2.6.5'并返回true并返回跟踪并运行函数,我们希望它返回false,然后当它到达开头时它会运行package(' python',V)和V将取另一个值。

...

Exit: (7) check_all(python, ['3.0', '2.4'], [lt, ge], '2.6.5') ? creep
V = '2.6.5' 

...

 Fail: (9) check_version(python, '2.4', ge, '2.6.5') ? creep
 Redo: (8) check_all(python, ['2.4'], [ge], '2.6.5') ? creep
 Call: (9) check_version(python, '2.4', ge, '2.6.5') ? creep
 Call: (10) package(python, '2.6.5') ? creep
 Exit: (10) package(python, '2.6.5') ? creep

当回溯跟踪时,在check_all中,它在check_all上失败,正如我们预期的那样,但是当它回溯check_version并运行package(python,'2.6.5')作为V = 2.6.5一个新值时它返回true。所以当V = 2.6.5时它再次返回true。有什么方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:3)

要本地化您的问题,请先缩小输入的大小。单个元素就足够了:

?- check_all('python', ['3.0'], [lt], V).

现在,哪些规则适用于单个元素?

两个规则都适用!所以删除更专业的那个。

还有另一种方法可以解决这样的问题。只需将规则相互比较,并尝试找出它们都适用的情况。最后一条规则适用于VerS = [],也适用于第一条规则。

答案 1 :(得分:2)

将谓词应用于列表的每个元素最好通过将列表作为其第一个参数的谓词来完成。如果参数是一个列表而不是一个变量(即当它是一个输入参数时),这就使得谓词在迭代完成时成功。你应该有两个条款:一个用于处理空列表,一个用于一般情况:

foo([]). % succeed
foo([X|Xs]) :-
    /* apply a predicate to X */
    foo(Xs). % apply predicate to the rest of the list

这里重要的是你不需要第三个只处理一个元素的列表的子句,因为带有一个元素的列表实际上是一个带有元素和空列表作为尾部的列表: / p>

?- [a] == [a|[]].
true.

?- [a] = [a|[]].
true.

另一个重要的事情是,在基本情况下,你应该做什么,空列表(至少在你的例子中)。

现在解决问题:你的输入是

  • 包名称
  • 两个列表,其中包含您在其他位置定义的谓词的参数对(cmp_version/3)。这是您的条件清单。

实现:

  • 已知的包可以作为事实:它们可以通过回溯来枚举。
  • 条件是一个输入参数,以列表形式提供:您需要将条件应用于列表的每个元素。

谓词:

check_all([], [], _, _).
check_all([V|Vs], [C|Cs], Name, Version) :-
    package(Name, V), % enumerate all known packages by backtracking
    cmp_version(Version, V, Cmp), % condition
    check_all(Vs, Cs, Name, Version). % apply condition to the rest of the list(s)

您应该阅读maplist的文档。您可以将查询表达为:

?- maplist(check_version(python), ['3.0', '2.4'], [lt, ge], Versions).

您已经定义了类似于:

的谓词check_version/4
check_version(Name, V, Cmp, Version) :-
    package(Name, Version),
    cmp_version(Version, V, Cmp).

作为旁注,maplist将重新排序其参数,使其行为与上面的显式迭代相似。

修改

@ mat的注释之后的命名问题:一个非常有用的命名约定是使用一个名称,该名称具有描述性的单字名称,由下划线分隔。例如,package/2变为package_version/2,因为它的第一个参数是包,第二个参数是版本。