列表中的数字小于给定数字

时间:2015-08-27 22:04:55

标签: list prolog

xMenores(_,[],[]).
xMenores(X,[H|T],[R|Z]) :-
   xMenores(X,T,Z),
   X > H,
   R is H.

xMenores有三个参数:

  • 第一个是数字。
  • 第二个是数字列表。
  • 第三个是列表,是包含结果的变量。

规则xMenores的目标是获得一个列表,其中列表的编号(第二个参数)小于第一个参数的值。例如:

?- xMenores(3,[1,2,3],X).
X = [1,2].                        % expected result

问题是当xMenores为假时false会返回X > H,而我的编程技巧在prolog中几乎为空。所以:

?- xMenores(4,[1,2,3],X).
X = [1,2,3].                      % Perfect.

?- xMenores(2,[1,2,3],X).
false.                            % Wrong! "X = [1]" would be perfect.

我考虑X > H, R is H.,因为只要X大于HRH的值,我就需要这样做。但我不知道像Prolog中的if或者某种控制结构来处理这个问题。

请问,任何解决方案?感谢。

7 个答案:

答案 0 :(得分:13)

使用( if -> then ; else )

您可能正在寻找的控制结构是( if -> then ; else )

警告:您应该交换前两个参数的顺序:

lessthan_if([], _, []).
lessthan_if([X|Xs], Y, Zs) :-
    (   X < Y
    ->  Zs = [X|Zs1]
    ;   Zs = Zs1
    ),
    lessthan_if(Xs, Y, Zs1).

但是,如果您正在编写实际代码,那么您几乎肯定应该使用library(apply)中的一个谓词,例如include/3,作为suggested by @CapelliC

?- include(>(3), [1,2,3], R).
R = [1, 2].

?- include(>(4), [1,2,3], R).
R = [1, 2, 3].

?- include(<(2), [1,2,3], R).
R = [3].

如果您想知道如何解决此类问题,请参阅the implementation of include/3。您会注意到上面的lessthan/3只是库中更一般include/3的特化(应用):include/3会重新排序参数并使用( if -> then ; else )

“陈述性”解决方案

或者,较少的“程序性”和更具“声明性”的谓词:

lessthan_decl([], _, []).
lessthan_decl([X|Xs], Y, [X|Zs]) :- X < Y,
    lessthan_decl(Xs, Y, Zs).
lessthan_decl([X|Xs], Y, Zs) :- X >= Y,
    lessthan_decl(Xs, Y, Zs).

lessthan_if/3lessthan_decl/3几乎与solutions by Nicholas Carey完全相同,但参数的顺序除外。)

在不利方面,lessthan_decl/3留下了选择点。但是,它是一个通用的可读解决方案的良好起点。我们需要两个代码转换:

  1. 将算术比较<>=替换为CLP(FD)约束:#<#>=;
  2. 使用DCG规则删除定义中的参数。
  3. 您将到达solution by lurker

    一种不同的方法

    Prolog中最常见的比较谓词是compare/3。使用它的一个常见模式是明确枚举Order的三个可能值:

    lessthan_compare([], _, []).
    lessthan_compare([H|T], X, R) :-
        compare(Order, H, X),
        lessthan_compare_1(Order, H, T, X, R).
    
    lessthan_compare_1(<, H, T, X, [H|R]) :-
        lessthan_compare(T, X, R).
    lessthan_compare_1(=, _, T, X, R) :-
        lessthan_compare(T, X, R).
    lessthan_compare_1(>, _, T, X, R) :-
        lessthan_compare(T, X, R).
    

    (与任何其他解决方案相比,这个解决方案适用于任何术语,而不仅仅是整数或算术表达式。)

    zcompare/3替换compare/3

    :- use_module(library(clpfd)).
    
    lessthan_clpfd([], _, []).
    lessthan_clpfd([H|T], X, R) :-
        zcompare(ZOrder, H, X),
        lessthan_clpfd_1(ZOrder, H, T, X, R).
    
    lessthan_clpfd_1(<, H, T, X, [H|R]) :-
        lessthan_clpfd(T, X, R).
    lessthan_clpfd_1(=, _, T, X, R) :-
        lessthan_clpfd(T, X, R).
    lessthan_clpfd_1(>, _, T, X, R) :-
        lessthan_clpfd(T, X, R).
    

    这绝对是比任何其他解决方案更多的代码,但它不会留下不必要的选择点:

    ?- lessthan_clpfd(3, [1,3,2], Xs).
    Xs = [1, 2]. % no dangling choice points!
    

    在其他情况下,它的表现与lurker的DCG解决方案一样:

    ?- lessthan_clpfd(X, [1,3,2], Xs).
    Xs = [1, 3, 2],
    X in 4..sup ;
    X = 3,
    Xs = [1, 2] ;
    X = 2,
    Xs = [1] ;
    X = 1,
    Xs = [] .
    
    ?- lessthan_clpfd(X, [1,3,2], Xs), X = 3. %
    X = 3,
    Xs = [1, 2] ; % no error!
    false.
    
    ?- lessthan_clpfd([1,3,2], X, R), R = [1, 2].
    X = 3,
    R = [1, 2] ;
    false.
    

    除非你需要这样一般的方法,include(>(X), List, Result)已经足够了。

答案 1 :(得分:10)

这也可以使用DCG来完成:

less_than([], _) --> [].
less_than([H|T], N) --> [H], { H #< N }, less_than(T, N).
less_than(L, N) --> [H], { H #>= N }, less_than(L, N).

| ?- phrase(less_than(R, 4), [1,2,3,4,5,6]).

R = [1,2,3] ? ;

您可以将谓词编写为:

xMenores(N, NumberList, Result) :- phrase(less_than(Result, N), NumberList).

答案 2 :(得分:5)

您可以使用findall\3

将其编写为单行
filter( N , Xs , Zs ) :- findall( X, ( member(X,Xs), X < N ) , Zs ) .

但是,我怀疑这个练习的目的是学习递归,所以这样的事情会起作用:

filter( _ , []     , []     ) .
filter( N , [X|Xs] , [X|Zs] ) :- X <  N , filter(N,Xs,Zs) .
filter( N , [X|Xs] , Zs     ) :- X >= N , filter(N,Xs,Zs) .
但是,它会在回溯时将列表解包两次。这里的优化是通过引入 soft cut 来组合第2和第3个子句,如下所示:

filter( _ , []     , []     ) .
filter( N , [X|Xs] , [X|Zs] ) :-
  ( X < N -> Zs = [X|Z1] ; Zs = Z1 ) ,
  filter(N,Xs,Zs)
  .

答案 3 :(得分:4)

(这更像是评论而不是答案,但评论的时间太长了。)

以前的一些答案和评论建议使用&#34; if-then-else&#34; (->)/2或使用library(apply) include/3。两种方法都可以正常工作,只要使用普通的Prolog算法 - is/2(>)/2等等......

?- X = 3, include(>(X),[1,3,2,5,4],Xs).
X = 3, Xs = [1,2].

?-        include(>(X),[1,3,2,5,4],Xs), X = 3.
ERROR: >/2: Arguments are not sufficiently instantiated
% This is OK. When instantiation is insufficient, an exception is raised.

...,但是当从(>)/2(#>)/2进行看似良性的转换时,我们会失去稳健性!

?- X = 3, include(#>(X),[1,3,2,5,4],Xs).
X = 3, Xs = [1,2].

?-        include(#>(X),[1,3,2,5,4],Xs), X = 3.
false.
% This is BAD! Expected success with answer substitutions `X = 3, Xs = [1,2]`.

答案 4 :(得分:4)

此答案中未提供新代码

在下文中,我们将详细介绍this answer by @lurker的不同修订版。

Revision #1 ,重命名为less_than_ver1//2。通过使用,代码既可读又通用

less_than_ver1(_, []) --> [].
less_than_ver1(N, [H|T]) --> [H], { H #< N }, less_than_ver1(N, T).
less_than_ver1(N, L) --> [H], { H #>= N }, less_than_ver1(N, L).

让我们查询!

?-  phrase(less_than_ver1(N,Zs),[1,2,3,4,5]).
  N in 6..sup, Zs = [1,2,3,4,5]
; N  = 5     , Zs = [1,2,3,4]
; N  = 4     , Zs = [1,2,3]
; N  = 3     , Zs = [1,2]
; N  = 2     , Zs = [1]
; N in inf..1, Zs = []
; false.

?- N = 3, phrase(less_than_ver1(N,Zs),[1,2,3,4,5]).
  N = 3, Zs = [1,2]       % succeeds, but leaves useless choicepoint
; false.

?-        phrase(less_than_ver1(N,Zs),[1,2,3,4,5]), N = 3.
  N = 3, Zs = [1,2]
; false.

作为小缺陷less_than_ver1//2会留下一些无用的选择点。

让我们看看新版本如何发展...

Revision #3 ,重命名为less_than_ver3//2

less_than_ver3([],_) --> [].
less_than_ver3(L,N) --> [X], { X #< N -> L=[X|T] ; L=T }, less_than_ver3(L,N).

此代码使用if-then-else((->)/2 + (;)/2)来提高确定性。

让我们重新运行以上查询!

?- phrase(less_than_ver3(Zs,N),[1,2,3,4,5]).
  N in 6..sup, Zs = [1,2,3,4,5]
; false.                              % all other solutions are missing!

?- N = 3, phrase(less_than_ver3(Zs,N),[1,2,3,4,5]).
  N = 3, Zs = [1,2]                   % works as before, but no better.
; false.                              % we still got the useless choicepoint

?-        phrase(less_than_ver3(Zs,N),[1,2,3,4,5]), N = 3.
false.                                % no solution! 
                                      % we got one with revision #1!

惊喜!以前工作过的两个案例现在(有些)被打破了,地面案例中的确定性并不好...... 为什么?

  1. vanilla if-then-else经常会过早削减太多,这对于使用协同处理和/或约束的代码来说尤其成问题。

    请注意(*->)/2(a.k.a。&#34; soft-cut&#34;或if/3),票价只会好一点,不是很多!

  2. 由于if_/3永远不会减少(通常比)香草if-then-else (->)/2,因此不能在上面的代码中使用它来改善决定论。

  3. 如果您想将if_/3与约束结合使用,请退后一步,编写非的代码作为第一个镜头。

  4. 如果您像我一样懒惰,请考虑使用tfilter/3(#>)/3

答案 5 :(得分:3)

This answer by @Boris提出了一个逻辑上纯粹的解决方案,该解决方案利用clpfd:zcompare/3来帮助改善某些(地面)案例中的确定性。

在这个答案中,我们将探索不同的编码逻辑纯Prolog的方法,同时试图避免创建无用的选择点。

让我们开始使用zcompare/3(#<)/3

  • zcompare/3实施有限域变量的three-way comparison,并将三分法归为<=>之一。
  • 由于OP使用的包含标准是算术不足测试,我们建议使用 (#<)/3将二分法改为truefalse

考虑以下查询的答案:

?- zcompare(Ord,1,5), #<(1,5,B).
Ord = (<), B = true.    

?- zcompare(Ord,5,5), #<(5,5,B).
Ord = (=), B = false.   

?- zcompare(Ord,9,5), #<(9,5,B).
Ord = (>), B = false.    

请注意,对于要选择的所有项目,Ord = (<) B = true都会成立。

以下是基于的三种非解决方案的并排比较:

  • 左侧使用zcompare/3和第一个参数索引来处理三个案例<=>
  • 中间人使用(#<)/3和第一个参数索引来处理两个案例truefalse
  • 正确的人使用(#<)/3if_/3结合使用。

请注意,我们不需要在右栏中定义辅助谓词!

less_than([],[],_).       % less_than([],[],_).          % less_than([],[],_).
less_than([Z|Zs],Ls,X) :- % less_than([Z|Zs],Ls,X) :-    % less_than([Z|Zs],Ls,X) :-
   zcompare(Ord,Z,X),     %    #<(Z,X,B),                %    if_(Z #< X,
   ord_lt_(Ord,Z,Ls,Rs),  %    incl_lt_(B,Z,Ls,Rs),      %        Ls = [Z|Rs],
   less_than(Zs,Rs,X).    %    less_than(Zs,Rs,X).       %        Ls = Rs),
                          %                              %    less_than(Zs,Rs,X).   
ord_lt_(<,Z,[Z|Ls],Ls).   % incl_lt_(true ,Z,[Z|Ls],Ls). %
ord_lt_(=,_,   Ls ,Ls).   % incl_lt_(false,_,   Ls ,Ls). %    
ord_lt_(>,_,   Ls ,Ls).   %                              % 

接下来,让我们使用

  • 在右栏中,我们使用if_//3代替if_/3
  • 请注意和非解决方案的不同参数顺序:less_than([1,2,3],Zs,3) vs phrase(less_than([1,2,3],3),Zs)

以下实施对应于上述非代码:

less_than([],_) --> [].   % less_than([],_) --> [].      % less_than([],_) --> [].  
less_than([Z|Zs],X) -->   % less_than([Z|Zs],X) -->      % less_than([Z|Zs],X) -->  
   { zcompare(Ord,Z,X) }, %    { #<(Z,X,B) },            %    if_(Z #< X,[Z],[]),
   ord_lt_(Ord,Z),        %    incl_lt_(B,Z),            %    less_than(Zs,X).     
   less_than(Zs,X).       %    less_than(Zs,X).          %
                          %                              %  
ord_lt_(<,Z) --> [Z].     % incl_lt_(true ,Z) --> [Z].   % 
ord_lt_(=,_) --> [].      % incl_lt_(false,_) --> [].    %
ord_lt_(>,_) --> [].      %                              % 

OK!保存为最后的最佳...只需将 tfilter/3(#>)/3一起使用!

less_than(Xs,Zs,P) :-
   tfilter(#>(P),Xs,Zs).

答案 6 :(得分:1)

中的this previous answer变体是我们的出发点。

考虑辅助非终结符ord_lt_//2

ord_lt_(<,Z) --> [Z].
ord_lt_(=,_) --> [].
ord_lt_(>,_) --> [].

可以使用两个条件覆盖这三个子句:

  1. Ord = (<):应包含项目
  2. dif(Ord, (<))应该 包括在内。

我们可以使用if_//3表达这种“要么选择要么”:

less_than([],_) --> [].
less_than([Z|Zs],X) -->
   { zcompare(Ord,Z,X) },
   if_(Ord = (<), [Z], []),
   less_than(Zs,X).

因此ord_lt_//2变得多余。

净收益? 3 !-)