如何找到条款列表的最小值?

时间:2015-04-09 21:08:16

标签: list prolog minimum

我有以下术语列表

[t('L', 76), t('I', 73), t('V', 86), t('E', 69)]

我想在prolog中编写一个谓词,以便它以最小的第二个值返回该术语。即从上面的列表中它应该返回t('E', 69)

以下是我的尝试。但这不起作用。

minChar(L, Min) :-
    setof(t(_, A), member(t(_, A), L), Li),
    Li = [Min|_].

以下是为上述输入提供的输出。

?- minChar([t('L', 76), t('I', 73), t('V', 86), t('E', 69)], Min).
Min = t(_G14650, 69) ;
Min = t(_G14672, 73) ;
Min = t(_G14683, 76) ;
Min = t(_G14661, 86).

3 个答案:

答案 0 :(得分:4)

正如潜伏者所说,谓词不能以大写字母开头,所以先修复它。

这里有两个基本问题:首先关闭所有,第二行中的两个下划线指的是不同的变量,因此setof/3不知道你在模板和模板中都想要相同的变量member/2来电。

其次,setof对结果进行排序(这就是为什么你可以像这样提取最小值),但是你构建模板的方式,它会错误地排序。 swi-prolog中的排序使用standard order of terms definition,在您的情况下,您要对t(A, B)类型的复合词进行排序,其中A是原子,B是数字。这将按字典顺序对A进行排序,然后对B进行排序,这不是你想要的,你想要在B上排序。

当您想要使用与术语本身不相同的键对事物进行排序时,标准技巧是提取所需的键,将其与(-)/2仿函数绑定,然后对其进行排序。因此,对于您的示例,这应该有效:

minChar(L, Min) :- 
  setof(B-t(A, B), member(t(A, B), L), Li),
  Li = [_-Min|_].

请记住,在Prolog中,当你说X - Y时,你实际上并没有做任何减法,即使它看起来像你。您只需使用(-)/2仿函数将X和Y绑定在一起。如果您特别要求它,它只进行减法,但使用一些强制算术评估的运算符(例如=:=<>is。这就是Prolog中1+1 = 2为假的原因,因为=是一个统一运算符,并且不进行任何算术运算评估。

要明确:您不能 使用-,您可以使用您喜欢的任何仿函数。但是传统的做法是使用负函子来做这种事情。

编辑:同样,setof/3将回溯模板中找不到的任何自由变量,并且因为两个下划线不引用相同的自由变量,所以它将回溯每个可能的任务。第二个下划线,然后抛出该结果并为第一个下划线分配一个新的自由变量。这就是为什么你可以回溯结果并得到一堆你不知道它们来自哪里的匿名变量。

答案 1 :(得分:4)

您可以自己编写setof谓词,而不是使用在 O(n log n)(至少)中运行的minChar

minChar([X],X) :-
    !.
minChar([t(_,V1)|T],t(A2,V2)) :-
    minChar(T,t(A2,V2)),
    V2 < V1,
    !.
minChar([X|_],X).

或者你可以通过使用累加器来进一步提升性能:

minChar([X|T],Min) :-
    minChar(T,X,Min).

minChar([],X,X).
minChar([t(A2,V2)|T],t(_,V1),Min) :-
     V2 < V1,
     !,
     minChar(T,t(A2,V2),Min).
minChar([_|T],X,Min) :-
    minChar(T,X,Min).

代码的工作原理如下:首先将列表统一为[X|T],(显然必须至少有一个项目,否则没有最小值)。现在,您将X作为第一个最小值。您遍历列表,并在每次比较t(A2,V2)(列表的新 head )时使用t(A1,V1)(当前找到的最小值)。如果第二个属性V2小于V1,我们知道我们找到了新的最小值,我们继续使用该术语进行搜索。否则,使用旧的当前最小值继续执行任务。如果我们到达列表的末尾,我们只需返回当前最小值

另一个性能攻击是将空列表案例作为最后一个,并将当前最小值放在最小的案例中:

minChar([t(_,V2)|T],t(A1,V1),Min) :-
     V1 <= V2,
     !,
     minChar(T,t(A1,V1),Min).
minChar([X|T],_,Min) :-
    minChar(T,X,Min).
minChar([],X,X).

这是因为Prolog始终首先按定义的顺序执行谓词。它只会在您到达空列表的情况下(在列表的末尾)发生一次。在遗嘱之后,找到较小值的几率将大大降低。

答案 2 :(得分:1)

你是Prolog的初学者,所以试着去思考Prolog。

列表的最小值是多少?此列表的元素,此列表中没有其他元素更小。 所以你可以写

my_min(L, Min) :-
    member(Min, L),
    \+((member(X, L), X < Min)).

有人会说:&#34;效率不高!&#34;。是的,但我认为这是学习Prolog的好方法。

您应该根据您的情况调整此代码。

编辑我说适应

min_of_list(L, t(X,Y)) :-
    member(t(X, Y), L),
    \+((member(t(_, Z), L), Z < Y)).