Hie*_*tos 10 list generator range prolog
我对Prolog很新,并且越来越喜欢它.我想知道这个实现是否可以进一步推广或改进,以及它是否是惯用的Prolog代码?
%% range/2
range(End, List) :-
End > 0, !,
range_ascend(0, End, 1, List).
range(End, List) :-
End < 0,
range_descend(0, End, 1, List).
%% range/3
range(Start, End, List) :-
((Start =< End) ->
(range_ascend(Start, End, 1, List))
;
(range_descend(Start, End, 1, List))).
%% range/4 (+Start, +End, +Step, -List)
range(Start, End, Step, List) :-
((Start =< End) ->
(range_ascend(Start, End, Step, List))
;
(range_descend(Start, End, Step, List))).
range_descend(Start, End, _, []) :-
End >= Start, !.
range_descend(Start, End, Step, [Start|Rest]) :-
Next is Start - Step,
range_descend(Next, End, Step, Rest).
range_ascend(Start, End, _, []) :-
Start >= End, !.
range_ascend(Start, End, Step, [Start|Rest]) :-
Next is Start + Step,
range_ascend(Next, End, Step, Rest).
Run Code Online (Sandbox Code Playgroud)
实现的一个主要问题是它只能"单向"工作,即当你的代码End设置为某个值时你的代码运行良好,但是当它是变量时它不起作用:
?- range(X,[0,1,2,3]).
ERROR: >/2: Arguments are not sufficiently instantiated
Run Code Online (Sandbox Code Playgroud)
也许你不需要这样的行为,但是在Prolog中,你的谓词作为一种真正的关系,通常既有可取也有优雅,它以多种不同的方式运作.
但是,实现这样的谓词通常比实现它们以功能方式工作更困难,特别是如果你是Prolog的初学者.
我不打算详细介绍如何专门改进代码(我认为这对Code Review SE网站来说更像一个问题).然而,我在一个范围谓词下面呈现出比你更好的行为:
range(I, S, [I|R]) :-
I #=< S,
if_(I = S,
R = [],
( J #= I + 1,
range(J, S, R)
)
).
Run Code Online (Sandbox Code Playgroud)
这个谓词需要if_/3和(=)/3来自library(reif),以及library(clpfd)(你可以包含在你的程序中:- use_module(library(clpfd)).).
正如您所看到的,它比您的实现短得多,但在多种不同场景中也能很好地工作:
?- range(0,5,L). % Same behavior as your predicate
L = [0, 1, 2, 3, 4, 5].
?- range(-5,0,L). % Different behavior from your predicate, but logically sounder
L = [-5, -4, -3, -2, -1, 0]
?- range(1,S,[1,2,3,4,5]). % Finding the max of the range
S = 5 ;
false.
?- range(I,S,[1,2,3,4,5]). % Finding both the min and max of the range
I = 1,
S = 5 ;
false.
?- range(I,S,[1,2,X,Y,5]). % With variables in the range
I = 1,
S = 5,
X = 3,
Y = 4 ;
false.
?- range(1,S,L). % Generating ranges
S = 1,
L = [1] ;
S = 2,
L = [1, 2] ;
S = 3,
L = [1, 2, 3] ;
S = 4,
L = [1, 2, 3, 4] ;
…
?- range(I,1,L). % Generating ranges
I = 1,
L = [1] ;
I = 0,
L = [0, 1] ;
I = -1,
L = [-1, 0, 1] ;
I = -2,
L = [-2, -1, 0, 1] ;
…
?- range(I,S,L). % Generating all possible ranges
I = S,
L = [S],
S in inf..sup ;
L = [I, S],
I+1#=S,
S#>=I,
dif(I, S) ;
L = [I, _G6396, S],
I+1#=_G6396,
S#>=I,
dif(I, S),
_G6396+1#=S,
S#>=_G6396,
dif(_G6396, S) ;
…
Run Code Online (Sandbox Code Playgroud)
我想你可以看到这里显示了多少行为,以及只使用一个谓词访问所有行为的可行性.
该谓词使用Constraint Logic Programming(clpfd库).CLP允许写整数之间的关系(这是#=<和#=你在代码中看到,相对于经典的=<和is您在低级别的运算使用).这就是我们的大部分繁重工作,并允许编写关于整数的简短的声明性代码(你不能轻易做到is).
我建议你阅读Markus Triska 的Prolog算术章节,了解Prolog中的CLP算法,如果你打算认真使用Prolog,这肯定是你需要学习的一个主题(我希望我已经在这个答案中说明了).