【问题标题】:Numbers in a list smaller than a given number列表中小于给定数字的数字
【发布时间】:2015-11-22 11:23:58
【问题描述】:
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

问题是当X > H 为假时xMenores 返回false,而我的编程技能在序言中几乎为零。所以:

?- 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 大于HR 就需要H 的值。但我不知道 Prolog 中的 if 或其他东西之类的控制结构来处理这个问题。

请问有什么解决办法吗?谢谢。

【问题讨论】:

  • 在 SWI 中,试试 ?- include(>(2),[4,1,2,3],L)。
  • 是的。但关键是自己做,用我自己的代码,或者至少知道谁来解决这个问题。谢谢

标签: list prolog


【解决方案1】:

您可以使用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 来组合第二和第三子句,如下所示:

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

【讨论】:

  • 在 SWI 中“软剪辑”指的是(*-&gt;)/2,而不是(-&gt;)/2
  • 一个完美的答案 (+1),但请注意 library(apply) 中的谓词可能是比 findall+member 更好的单线方法,include/3 与您的上一个解决方案相同。
  • 谢谢,尼古拉斯的完美解决方案!
  • @repeat:这不是 SWI 特有的。软切割总是意味着 if/3(*-&gt;)/2 - 参见 Lee Naish 的 Prolog 中的否定和控制,LNCS 238
  • 在单行中使用tfilter/3 加入赏金活动!
【解决方案2】:

使用( if -&gt; then ; else )

您可能正在寻找的控制结构是( if -&gt; 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 -&gt; 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. 将算术比较 &lt;&gt;= 替换为 CLP(FD) 约束:#&lt;#&gt;=
  2. 使用 DCG 规则去除定义中的参数。

您将到达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).

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

compare/3 替换为zcompare/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(&gt;(X), List, Result) 就足够了。

【讨论】:

  • 谢谢!我接受了这个,因为它是对solution by Nicholas Carey 的改进。完美!
  • 虽然 include/3 经常暴露非关系行为,但这次是 - 使用完美的算术表达式。
  • @repeat 然后很难讨论不同的方法。将编辑此答案以更明确地说明每种解决方案的优缺点。
  • 考虑这两个查询:?- lessthan_compare([1,2,3],4,Xs).?- lessthan_compare([1,2,3],X,Xs), X=4.
  • 使用compare/3 的版本可能给出的答案并不适用于所使用的变量可以采用的所有可能值。看stackoverflow.com/questions/26720685/safe-term-order。使用zcompare/3 的变体安全的!
【解决方案3】:

这也可以使用 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).

【讨论】:

  • 在我看来,这是迄今为止在此线程中发布的最佳解决方案:它非常易读,也非常通用。 +1!当然,假设问题是针对整数提出的,从示例中似乎确实是这种情况,并且也是典型的。
  • s(X)。很好,非常正确,但效率低下。为什么不考虑使用if_/3 甚至if_//3 的答案?世界上没有任何实现能够使您的代码像现在确定的那样确定给定的 N 和列表。
  • @false 是的,我知道它效率低下且不确定。我提供它作为“查看 DCG 眼镜问题”的一种方式,但现在明示或暗示效率保证。关于 if_/3if_//3,我还没有(还)习惯使用它们。
  • 承诺:将尽快为这样的实施提供赏金!
  • @repeat 我使用这些谓词进行了几次尝试,但始终以选择点结束。我还没有回去解决它。
【解决方案4】:

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

之前的一些答案和 cmets 建议使用“if-then-else”(-&gt;)/2 或使用 library(apply) include/3。这两种方法都可以正常工作,只要只使用普通的 Prolog 算术——is/2(&gt;)/2 等等……

?- X = 3, 包括(>(X),[1,3,2,5,4],Xs)。 X = 3,Xs = [1,2]。 ?- 包括(>(X),[1,3,2,5,4],Xs), X = 3。 错误:> / 2:参数没有充分实例化 % OK。当实例化不充分时,引发异常。

...,但是当从(&gt;)/2(#&gt;)/2 进行看似良性的切换时,我们失去了稳定性!

?- X = 3, 包括(#>(X),[1,3,2,5,4],Xs)。 X = 3,Xs = [1,2]。 ?- 包括(#>(X),[1,3,2,5,4],Xs), X = 3错误。 % 糟糕! 预期成功,答案替换为 `X = 3, Xs = [1,2]`。

【讨论】:

  • 请参阅上面我原始答案的补充。我觉得这是不必要的,但既然你坚持:)
  • @Boris。好的。请解释一下“没有必要”是什么意思。
  • 好的,我把“不必要的”收回。不确定在什么情况下需要这样的谓词。
【解决方案5】:

此答案中没有新代码

下面我们详细了解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).

我们来查询吧!

?- 短语(less_than_ver1(N,Zs),[1,2,3,4,5])。 6..sup 中的 N,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 inf..1, Zs = [] ;错误的。 ?- N = 3, 短语(less_than_ver1(N,Zs),[1,2,3,4,5])。 N = 3, Zs = [1,2] % 成功,但留下无用的选择点 ;错误的。 ?- 短语(less_than_ver1(N,Zs),[1,2,3,4,5]), N = 3。 N = 3,Zs = [1,2] ;错误的。

作为一个小缺陷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 ((-&gt;)/2 + (;)/2) 来提高确定性。

让我们简单地重新运行上述查询!

?- 短语(less_than_ver3(Zs,N),[1,2,3,4,5])。 6..sup 中的 N,Zs = [1,2,3,4,5] ; 错误。 % 所有其他解决方案都丢失了! ?- N = 3, 短语(less_than_ver3(Zs,N),[1,2,3,4,5])。 N = 3, Zs = [1,2] % 像以前一样工作,但没有更好; 错误。 % 我们仍然得到了无用的选择点 ?- 短语(less_than_ver3(Zs,N),[1,2,3,4,5]),N = 3错误。 % 没有解决方案! % 我们得到了一个修订版 #1!

惊喜!以前有效的两个案例现在(有点)被打破了,基本案例中的确定性也好不到哪里去……为什么?

  1. 普通的 if-then-else 经常过早地削减太多,这对于使用协同程序和/或约束的代码尤其成问题。

    请注意,(*-&gt;)/2(又名“软剪辑”或 if/3)的票价只是稍微好一点,而不是很多!

  2. 由于 if_/3 永远不会削减更多(通常超过)vanilla if-then-else (-&gt;)/2,因此不能在上述代码中使用它来提高确定性。

  3. 如果您想将if_/3 与约束结合使用,请退后一步,首先编写非 的代码。

  4. 如果你像我一样懒惰,可以考虑使用,比如tfilter/3(#&gt;)/3

【讨论】:

  • 是的,对不起,我需要再喝一杯咖啡...不过,您的帖子对其分析非常有帮助 (+1)。
  • 好的,没有伤害。不要犹豫发布独立(附加)的新答案。在将焦点转移到确定性上时,首先考虑非 dcg 情况会更容易。
【解决方案6】:

This answer by @Boris 提出了一个逻辑纯的解决方案,它利用clpfd:zcompare/3 来帮助提高某些(基本)情况下的确定性。

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


让我们开始使用zcompare/3(#&lt;)/3

  • zcompare/3 实现有限域变量的three-way comparison 并将三分法具体化为&lt;=&gt; 之一。
  • 由于 OP 使用的纳入标准是算术小于测试,我们建议使用 (#&lt;)/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 = (&lt;) B = true 都成立。


这是基于 的三个非 解决方案的并排比较:

  • 左边使用zcompare/3&lt;=&gt; 三种情况的第一个参数索引。
  • 中间的使用(#&lt;)/3truefalse 两种情况的第一个参数索引。
  • 正确的使用(#&lt;)/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_(>,_) --> [].      %                              % 

好的!将最好的留到最后...只需将 tfilter/3(#&gt;)/3 一起使用!

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

【讨论】:

  • 我不是 100% 满意这个(#&gt;)/3
  • @false。对界面或实现不满意?
  • 当前的实现非常昂贵,因此我怀疑这是否是一个好主意。
  • @false。我知道了。我们在同一页面上。我认为 FD 物化也是巴洛克式的。但是哪里有 3 路分支点呢?在(#&gt;)/3 中看不到它。
  • zcompare/3 是一个合理的接口,而(#&gt;)/2 和家庭导致组合爆炸 - 高度非 DRY
【解决方案7】:

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

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

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

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

  1. Ord = (&lt;):该项目应该被包括在内。
  2. dif(Ord, (&lt;)):它应该 包括在内。

我们可以使用if_//3 来表达这种“非此即彼的选择”:

小于([],_)-> []。 小于([Z|Zs],X) --> { zcompare(Ord,Z,X) }, if_(Ord = (, 小于(Zs,X)。

因此ord_lt_//2 变得多余。

净收益? 3 !-)

【讨论】:

  • 我希望@lurker 回答这个问题
  • if_//3 是在哪里定义的? (链接)
  • @false。我也是。该链接在较旧的答案中,将相应地更新此答案...
猜你喜欢
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
  • 2021-11-01
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多