绘制星星 Prolog 最多 N:画一条线太少

Draw stars Prolog up to N: draws one line too less

带有参数 N 和 M 的谓词 draw/2 应该绘制 M 颗星,并且应该增加 N 直到 N > 5。示例:draw(3,5) =>

***
****
*****

我的问题是我的代码最多只能绘制四颗星,所以:

***
****

为什么 draw/2 中存在 M1 =< N 条件时会发生这种情况?

line(0,_) :- nl.
line(X, Symbol) :-
  write(Symbol),
  Line is X - 1,
  line(Line, Symbol).

% b)

draw(N, N).

draw(M, N) :-
  line(M, '*'),
  M1 is M + 1,
  M1 =< N,
  draw(M1, N).

问题是 draw/2 的第一个子句 draw(N, N). 在您达到第二个子句中的条件之前就成功了。

但是使用 between/3 完成整个练习会容易得多,因为它计算序言方式(包括下限和上限)。以下是一些示例:

?- between(3, 5, X).
X = 3 ;
X = 4 ;
X = 5.

?- forall(between(3, 5, X), format("~d~n", [X])).
3
4
5
true.

?- forall(between(3, 5, X), ( forall(between(1, X, _), write(*) ), nl )).
***
****
*****
true.

如果你不喜欢单行,你可以定义:

draw(N, M) :-
    forall(between(N, M, X),
        line(X)).

line(X) :-
    forall(between(1, X, _),
        write(*)),
    nl.

您绝对应该避免指望自己。因为编程有两个难点,第三个是差一错误。

使用 SWI-Prolog,您还可以使用格式 hack 直接打印必要数量的星星:

?- forall(between(3, 7, X), format("~`*t~*|~n", [X])).
***
****
*****
******
*******
true.

以某种方式记录了为什么这有效。

注意:forall/2 谓词定义为 \+ ( Cond, \+ Action )。所以你可以不用forall重写最后一个,直接写成:

?- \+ ( between(3, 5, X), \+ format("~`*t~*|~n", [X]) ).
***
****
*****
true.

这本身可以重写为“故障循环”,但这有一些缺点。

?- (   between(3, 5, X),         % generate solutions for X = 3 ; 4 ; 5
       format("~`*t~*|~n", [X]), % side effect
       fail                      % fail to backtrack to the next solution
    ;  true                      % when there are no more solutions, succeed
    ).
***
****
*****
true.

例如,失败驱动循环中的副作用失败会导致它失败(请参阅 forall/2 文档)。比较:

?- ( between(3, 5, X), fail, fail ; true ).
true.

?- forall(between(3, 5, X), fail).
false.

?- \+ ( between(3, 5, X), \+ fail ).
false.

如果你想向上计数,你可能想看看具有类似功能的库谓词的定义:numlist/3.

如果只采用numlist_/3中的逻辑,去掉最后一个参数,直接使用值打印一行,你会得到:


draw(U, U) :-
    !,
    line(U).
draw(L, U) :-
    line(L),
    L2 is L+1,
    draw(L2, U).

但是您需要添加参数检查。你可以这样做:

line(0) :-
    !,
    nl.
line(X) :-
    succ(X0, X),
    write(*),
    line(X0).

draw(L, U) :-
    L =:= U,
    !,
    line(U).
draw(L, U) :-
    L < U,
    line(L),
    L2 is L+1,
    draw(L2, U).