首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >改进Prolog中范围内的列表生成

改进Prolog中范围内的列表生成
EN

Stack Overflow用户
提问于 2017-05-04 07:36:47
回答 1查看 457关注 0票数 10

我对Prolog相当陌生,并且越来越爱上它。我想知道这个实现是否可以进一步推广或改进,以及它是否是惯用的Prolog代码?

代码语言:javascript
复制
%% 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).
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-04 09:38:10

实现的主要问题之一是它只能“单向”工作,即当End设置为某个值时,代码运行良好,但是当它是变量时,代码就不能工作了:

代码语言:javascript
复制
?- range(X,[0,1,2,3]).
ERROR: >/2: Arguments are not sufficiently instantiated

也许您不需要这样的行为来工作,但是在Prolog中,谓词作为一个真正的关系通常是可取的和优雅的,它以多种不同的方式工作。

然而,实现谓词本身通常比以功能方式实现它们要困难得多,特别是如果您是Prolog的初学者。

我不打算详细讨论如何具体地改进您的代码(我认为这更像是代码审查 SE站点的一个问题)。不过,我在下面介绍的是一个范围谓词,它的行为比您的更好:

代码语言:javascript
复制
range(I, S, [I|R]) :-
    I #=< S,
    if_(I = S,
        R = [],
        (   J #= I + 1,
            range(J, S, R)
        )
    ).

这个谓词需要来自if_/3(=)/3library(reif),以及library(clpfd) (您可以将其包含在使用:- use_module(library(clpfd)).的程序中)。

正如您所看到的,它比您的实现要短得多,但在多个不同的场景中也工作得很好:

代码语言:javascript
复制
?- 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) ;
…

我认为您可以看到这里显示了多少行为,以及只使用一个谓词访问所有这些行为是多么有用。

该谓词使用约束逻辑编程( clpfd库)。CLP允许在整数之间写入关系(这是您在代码中看到的#=<#=,而不是您在低级算术中使用的经典=<is )。这是我们需要做的大部分工作,并允许编写简短的关于整数的声明性代码(使用is是很难做到的)。

我建议您阅读Markus的Prolog算法章节的威力,以了解Prolog中的CLP算法,如果您想认真使用Prolog,这绝对是您需要学习的一个主题(我希望我在这个答案中已经说明了)。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43776669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档