绘制星星 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).
带有参数 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).