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
大于H
,R
取H
的值,我就需要这样做。但我不知道像Prolog中的if或者某种控制结构来处理这个问题。
请问,任何解决方案?感谢。
答案 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/3
和lessthan_decl/3
几乎与solutions by Nicholas Carey完全相同,但参数的顺序除外。)
在不利方面,lessthan_decl/3
留下了选择点。但是,它是一个通用的可读解决方案的良好起点。我们需要两个代码转换:
<
和>=
替换为CLP(FD)约束:#<
和#>=
; 您将到达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)
meta-predicate 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
。通过使用dcg和clpfd,代码既可读又通用:
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!
惊喜!以前工作过的两个案例现在(有些)被打破了,地面案例中的确定性并不好...... 为什么?
vanilla if-then-else经常会过早削减太多,这对于使用协同处理和/或约束的代码来说尤其成问题。
请注意(*->)/2
(a.k.a。&#34; soft-cut&#34;或if/3
),票价只会好一点,不是很多!
由于if_/3
永远不会减少(通常比)香草if-then-else (->)/2
,因此不能在上面的代码中使用它来改善决定论。
如果您想将if_/3
与约束结合使用,请退后一步,编写非dcg的代码作为第一个镜头。
如果您像我一样懒惰,请考虑使用tfilter/3
和(#>)/3
等meta-predicate。
答案 5 :(得分:3)
This answer by @Boris提出了一个逻辑上纯粹的解决方案,该解决方案利用clpfd:zcompare/3
来帮助改善某些(地面)案例中的确定性。
在这个答案中,我们将探索不同的编码逻辑纯Prolog的方法,同时试图避免创建无用的选择点。
让我们开始使用zcompare/3
和(#<)/3
!
zcompare/3
实施有限域变量的three-way comparison,并将三分法归为<
,=
或>
之一。(#<)/3
将二分法改为true
或false
。考虑以下查询的答案:
?- 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
和第一个参数索引来处理两个案例true
和false
。(#<)/3
与if_/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). % %
接下来,让我们使用dcg!
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!保存为最后的最佳...只需将meta-predicate tfilter/3
与(#>)/3
一起使用!
less_than(Xs,Zs,P) :-
tfilter(#>(P),Xs,Zs).
答案 6 :(得分:1)
dcg中的this previous answer变体是我们的出发点。
考虑辅助非终结符ord_lt_//2
:
ord_lt_(<,Z) --> [Z].
ord_lt_(=,_) --> [].
ord_lt_(>,_) --> [].
可以使用两个条件覆盖这三个子句:
Ord = (<)
:应包含项目 。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 lines-of-code!-)