【问题标题】:Find the minimum in a mixed list in Prolog在 Prolog 的混合列表中查找最小值
【发布时间】:2017-03-20 06:51:26
【问题描述】:

我是 prolog 的新手,我刚刚学习列表,我遇到了this question。答案非常适合整数列表。

minimo([X], X) :- !.
minimo([X,Y|Tail], N):-
    ( X > Y ->
        minimo([Y|Tail], N)
    ;
        minimo([X|Tail], N)
    ).

如何更改此代码以从混合列表中获取最小的 int?

这个

sint([a,b,3,2,1],S)

应该给出答案:

S=1

【问题讨论】:

  • 没有整数怎么办?

标签: prolog


【解决方案1】:

您可以忽略这个问题,将比较运算符 (>)/2(实际上是二进制内置谓词)更改为更通用的 (@>)/2:

minimo([X], X) :- !.
minimo([X,Y|Tail], N):-
    ( X @> Y ->
        minimo([Y|Tail], N)
    ;
        minimo([X|Tail], N)
    ).

?- minimo([a,b,3,2,1],S).
S = 1.

【讨论】:

  • 这对于不包含整数的列表也会成功,例如minimo([a])
  • 我知道...不要认为这是个问题
  • 我也在考虑这些思路 (s(X)),但让我感到困扰的是,如果将问题更改为最大值而不是最小值,对称解决方案将不起作用。
【解决方案2】:

首先,我不认为提议的实现非常优雅:在这里,它们通过每次构造一个新列表来传递迄今为止找到的最小元素。使用附加参数(我们称为 accumulator)通常是可行的方法(并且可能也更有效)。

为了解决这个问题,我们首先必须找到一个整数。我们可以这样做:

sint([H|T],R) :-
    integer(H),
    !,
    sint(T,H,R).
sint([_|T],R) :-
    sint(T,R).

所以我们在这里检查 head H 是否是 integer/1。如果是这种情况,我们称谓词sint/3(不要与sint/2 混淆)。否则,我们使用列表的 tail T 递归调用 sint/2

现在我们仍然需要定义sint/3。如果我们已经到达列表的末尾[],我们只需返回到目前为止找到的最小值:

sint([],R,R).

否则有两种情况:

  • head H 是一个整数,并且比目前找到的元素小,在这种情况下,我们将 head 作为新的当前最小值执行递归:

    sint([H|T],M,R):
        integer(H),
        H < M,
        !,
        sint(T,H,R).
    
  • 否则,我们只是忽略头部,并使用尾部T进行递归。

    sint([_|T],M,R) :-
        sint(T,M,R).
    

我们可以将递归子句放在 if-then-else 结构中。加上前面定义的谓词,完整的程序是:

sint([H|T],R) :-
    integer(H),
    !,
    sint(T,H,R).
sint([_|T],R) :-
    sint(T,R).

sint([],R,R).
sint([H|T],M,R):
    (
     (integer(H),H < M)
     -> sint(T,H,R)
     ;  sint(T,M,R)
    ).

这种方法的优点是过滤比较(以获得最小值)是同时完成的,所以我们只在列表上迭代一次。这通常会导致性能提升,因为“控制结构”只执行一次:在迭代中完成更多工作,但我们只迭代一次。

我们可以通过使 filter 通用化来概括该方法:

filter_minimum(Filter,[H|T],R) :-
    Goal =.. [Filter,H],
    call(Goal),
    !,
    filter_minimum(Filter,T,H,R).
filter_minimum(Filter,[_|T],R) :-
    filter_minimum(Filter,T,R).

filter_minimum(_,[],R,R).
filter_minimum(Filter,[H|T],M,R) :-
    Goal =.. [Filter,H],
    (
     (call(Goal),H < M)
     -> filter_minimum(Filter,T,H,R)
     ;  filter_minimum(Filter,T,M,R)
    ).

然后你可以调用它:

filter_minimum(integer,[a,b,3,2,1],R).

integer/1 过滤并计算最小值。

【讨论】:

    【解决方案3】:

    你可以只写一个谓词返回一个带有数字的列表并使用上面的 minimo/2 谓词:

    only_numbers([],[]).
    only_numbers([H|T],[H|T1]):-integer(H),only_numbers(T,T1).
    only_numbers([H|T],L):- \+integer(H),only_numbers(T,L).
    
    sint(L,S):-only_numbers(L,L1),minimo(L1,S).
    

    【讨论】:

    • number/1 也接受浮点数:number(14.25) 成功。
    • @Willem Van Onsem,谢谢!!使用 integer/1 而不是 number/1 可能会更好...
    • 然后,使用 include/3: only_numbers(L,Ns) :- include(integer,L,Ns).
    猜你喜欢
    • 2017-09-19
    • 2011-04-27
    • 2013-11-16
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    • 2016-03-15
    相关资源
    最近更新 更多