为什么Prolog中的min()函数不起作用?

时间:2014-01-07 09:30:30

标签: debugging prolog min

这是我在Prolog中的min()函数,它应该找到数字列表中的最小元素。我的想法是测试Result是否小于或等于列表中的每个元素。列表的头部将被删除,直到它为空。达到此点时,结果中的设置是正确的。

min([], Result).
min([Head|Tail], Result) :-
    Result =< Head,
    min(Tail, Result).

在SWI-Prolog中查询此文件时,它可以测试值是否是min([5, 3, 7, 6], 3)返回true的最小元素。但min([5, 3, 7, 6], X)找不到X的值,只返回true

如何让它找到X的值?

4 个答案:

答案 0 :(得分:2)

无法设置空列表的最小值。 你可以写

min([X], X). 

希望X是一个整数;我认为是! 那么

min([Head|Tail], Result) :-
    % you fetch the min of the rest of the list
    min(Tail, R1),
    (Head < R1
    -> Result = Head
    ;  Result = R1).

答案 1 :(得分:2)

我的SWI-Prolog报告

?- min([5, 3, 7, 6], X).
ERROR: user://1:18:
    =</2: Arguments are not sufficiently instantiated

这是有道理的,因为第一个X =< 3是使用未绑定的X执行的。要使谓词起作用,您需要遵循最小值的定义,该定义未针对空列表定义:

min([X], X).
min([X|L], Min) :-
    L = [_|_],    % non-empty tail; could be replaced by a cut
    min(L, MinL),
    Min is min(X, MinL).

(请注意,最后一行会退回到内置min,由is进行评估。)

或者,您可以获取元素对的最小值,给出

min([X], X).
min([X,Y|L], Min) :-
    MinXY is min(X, Y),
    min([MinXY|L], Min).

这个版本是尾递归的。

答案 2 :(得分:2)

如果你打开跟踪,你可以看到你的脚本出了什么问题。

1 ?- trace.
true.

原始版本:

/** Version 0 */
min([], Result).
min([Head|Tail], Result) :-
    Result =< Head,
    min(Tail, Result).

输出:

[trace] 2 ?- min([5,3,7,6],X).
   Call: (6) min([5, 3, 7, 6], _G4129) ? creep
   Call: (7) _G4129=<5 ? creep
ERROR: =</2: Arguments are not sufficiently instantiated
   Exception: (7) _G4129=<5 ? creep

您试图将未实例化的变量与5进行比较。解决方案:在脚本中交换行,以便在比较之前实例化变量。

/** Version 1 */
min([], Result).
min([Head|Tail], Result) :-
    min(Tail, Result),
    Result =< Head.

输出:

[trace] 5 ?- min([5,3,7,6],X).
   Call: (6) min([5, 3, 7, 6], _G517) ? creep
   Call: (7) min([3, 7, 6], _G517) ? creep
   Call: (8) min([7, 6], _G517) ? creep
   Call: (9) min([6], _G517) ? creep
   Call: (10) min([], _G517) ? creep
   Exit: (10) min([], _G517) ? creep
   Call: (10) _G517=<6 ? creep
ERROR: =</2: Arguments are not sufficiently instantiated
   Exception: (10) _G517=<6 ?
[trace] 102 ?- min([5,3,7,6],X).
   Call: (6) min([5, 3, 7, 6], _G3067) ? creep
   Call: (7) min([3, 7, 6], _G3067) ? creep
   Call: (8) min([7, 6], _G3067) ? creep
   Call: (9) min([6], _G3067) ? creep
   Call: (10) min([], _G3067) ? creep
   Exit: (10) min([], _G3067) ? creep
   Call: (10) _G3067=<6 ? creep
ERROR: =</2: Arguments are not sufficiently instantiated
   Exception: (10) _G3067=<6 ? 

这样脚本会更进一步但是当计算尾部的最小值时,它再次与未实例化的变量进行比较。解决方案:将min([], Result).更改为min([Result], Result)

/** version 2 */   
min([Result], Result).
min([Head|Tail], Result) :-
    min(Tail, Result),
    Result =< Head.

输出:

[trace] 8 ?- min([5,3,7,6],3).
   Call: (6) min([5, 3, 7, 6], 3) ? creep
   Call: (7) min([3, 7, 6], 3) ? creep
   Call: (8) min([7, 6], 3) ? creep
   Call: (9) min([6], 3) ? creep
   Call: (10) min([], 3) ? creep
   Fail: (10) min([], 3) ? creep
   Fail: (9) min([6], 3) ? creep
   Fail: (8) min([7, 6], 3) ? creep
   Fail: (7) min([3, 7, 6], 3) ? creep
   Fail: (6) min([5, 3, 7, 6], 3) ? creep
false.

该程序现在只返回false。这是因为你只考虑尾部的最小值小于或等于头部的情况。当输入列表按顺序排序时,此程序将仅返回正确的结果(以便尾部的最小值小于头部,递归)。

[trace] 10 ?- min([5,4,3,2],X).
   Call: (6) min([5, 4, 3, 2], _G2469) ? creep
   Call: (7) min([4, 3, 2], _G2469) ? creep
   Call: (8) min([3, 2], _G2469) ? creep
   Call: (9) min([2], _G2469) ? creep
   Exit: (9) min([2], 2) ? creep
   Call: (9) 2=<3 ? creep
   Exit: (9) 2=<3 ? creep
   Exit: (8) min([3, 2], 2) ? creep
   Call: (8) 2=<4 ? creep
   Exit: (8) 2=<4 ? creep
   Exit: (7) min([4, 3, 2], 2) ? creep
   Call: (7) 2=<5 ? creep
   Exit: (7) 2=<5 ? creep
   Exit: (6) min([5, 4, 3, 2], 2) ? creep
X = 2 .

因此,当尾部的最小值严格大于头部时,你需要通过添加以下条款来处理这种情况:

min([Head|Tail], Result) :-
    min(Tail, R1),
    Head < R1,
    Result is Head.

这是最终版本:

/** version 3 */
min([Result], Result).
min([Head|Tail], Result) :-
    min(Tail, Result),
    Result =< Head.

min([Head|Tail], Result) :-
    min(Tail, R1),
    Head < R1,
    Result is Head.

(使用nodebug.转换跟踪)

答案 3 :(得分:1)

它不可能在那里返回trueX =< 3会导致“ERROR: =</2: Arguments are not sufficiently instantiated”。也许您使用@=<进行比较,但只需成功X @=< 3就可以成功,而无需以任何方式实例化X。因此,X将保持不变,min([], X)最终会成功 - 正是您所观察到的。

你的代码也是另一种错误。 min([5, 3, 7, 6], 0)也取得了成功,但不应该成功。

其他人为您提供了递归解决方案。对于迭代的,我们通常会添加另一个accumulating参数,并将链中的Result传递给我们,以便从累加器中为我们接收最终值:

min([H|T], Result):- min(T,H,Result).

min([], Result, Result).
min([Head|Tail], M, Result) :-
    M =< Head -> min(Tail, M, Result)
    ;            min(Tail, Head, Result).

空列表没有最小值。参数列表的头部用作最小值的初始值。