【发布时间】:2021-08-05 13:39:23
【问题描述】:
如何在 prolog 中编写一个程序来打印范围之间的偶数和奇数?
样本输出:
Enter first number:2
Enter Second number:15
Even numbers:
2
4
6
8
10
12
14
Odd numbers:
1
3
5
7
9
11
13
15
【问题讨论】:
标签: prolog
如何在 prolog 中编写一个程序来打印范围之间的偶数和奇数?
样本输出:
Enter first number:2
Enter Second number:15
Even numbers:
2
4
6
8
10
12
14
Odd numbers:
1
3
5
7
9
11
13
15
【问题讨论】:
标签: prolog
这是一种方法:
odds_and_evens( H , H , [H] , [] ) :- % if lo and hi have converged,
0 =\= H rem 2 % and it's odd
. % then park it with the odds and succeed.
odds_and_evens( H , H , [] , [H] ) :- % if lo and hi have conveged,
0 =:= H rem 2 % and it's even
. % then park it with the evens and succeed.
odds_and_evens( L , H, [L|Odds] , Evens ) :- % else... park lo with the odds
L < H, % - if lo is less than hi
0 =\= L rem 2, % - and lo is odd
L1 is L+1, % - increment lo
odds_and_evens(L1, H, Odds, Evens ) % - and recurse down
. %
odds_and_evens( L , H, Odds , [H|Evens] ) :- % else... park lo with the evens
L < H, % - if lo is less than hi
0 =:= L rem 2, % - and lo is even
L1 is L+1, % - incement lo
odds_and_evens(L1, H, Odds, Evens ) % - and recurse down
. % Easy!
但是... Prolog 有一个内置的between/3,它会生成一个整数范围。 findall/3 将查询的所有解决方案作为列表查找。所以也可以——而且更简洁、更明确! ——说:
odds_and_evens( L , H , Odds , Evens ) :-
findall( X , odds( L , H , X ) , Odds ),
findall( X , evens( L , H , X ) , Evens )
.
odds( L , H , N ) :- between(L,H,N), 0 =\= N rem 2 .
evens( L , H , N ) :- between(L,H,N), 0 =:= N rem 2 .
一旦你定义了odds_and_evens/4,你就可以这样做了:
print_odds_and_evens( Lo, Hi ) :-
odds_and_evens(Lo,Hi,Odds,Evens),
write_to_console('Odd Numbers', Odds ),
write_to_console('Even Numbers', Evens )
.
write_to_console(Label, Ns ) :-
write(Label), writeln(':'),
write_to_console(Ns)
,
write_to_console([]) .
write_to_console([N|Ns]) :- writeln(N), write_to_console(Ns).
如果这对您来说递归太多,您也可以说(从上面抄写odds/3 和evens/3):
print_odds_and_evens(Lo,Hi) :-
writeln('Odd Numbers:'),
odd(Lo,Hi,N),
writeln(N),
fail
.
print_odds_and_evens(Lo,Hi) :-
writeln('Even Numbers:'),
even(Lo,Hi,N),
writeln(N),
fail
.
print_odds_and_evens(_,_).
odd( L , H , N ) :- between(L,H,N), 0 =\= N rem 2 .
even( L , H , N ) :- between(L,H,N), 0 =:= N rem 2 .
【讨论】:
\+ ( Generator, \+ Side_effect ) 作为更清晰的故障驱动循环。
这是基于 SWI-Prolog partition/4 的答案。
example_1(Odd,Even) :-
numlist(1,15,List),
partition(is_even,List,Even,Odd).
is_even(N) :-
0 is N mod 2.
示例运行
?- example_1(Odd,Even).
Odd = [1, 3, 5, 7, 9, 11, 13, 15],
Even = [2, 4, 6, 8, 10, 12, 14].
这是一个将支持谓词放入Lambda 的变体。
example_2(Odd,Even) :-
numlist(1,15,List),
partition([N]>>(0 is N mod 2),List,Even,Odd).
示例运行
?- example_2(Odd,Even).
Odd = [1, 3, 5, 7, 9, 11, 13, 15],
Even = [2, 4, 6, 8, 10, 12, 14].
有关输入起始值和结束值的多部分问题的另一部分的答案,我建议使用read_string/5。
见:
如何将用户控制台输入到 Prolog 列表 (Answer)
Prolog:如何从控制台读取数据并存储到数据库中。 (Answer)
Other Answers
【讨论】:
这是一个仅使用 Prolog ISO 标准内置谓词的解决方案。这也是一个快速的解决方案,因为它避免了元谓词和附加列表:
print_even_odd(Lower, Upper) :-
even_odd(Lower, Upper, Even, Odd),
write('Even: '), write(Even), nl,
write('Odd: '), write(Odd), nl.
even_odd(Lower, Upper, Even, Odd) :-
Lower =< Upper,
( Lower mod 2 =:= 0 ->
even_odd_lists(Lower, Upper, Even, Odd)
; even_odd_lists(Lower, Upper, Odd, Even)
).
even_odd_lists(Upper, Upper, [Upper], []) :-
!.
even_odd_lists(N, Upper, [N| Even], Odd) :-
M is N + 1,
even_odd_lists(M, Upper, Odd, Even).
示例调用:
| ?- print_even_odd(1, 24).
Even: [2,4,6,8,10,12,14,16,18,20,22,24]
Odd: [1,3,5,7,9,11,13,15,17,19,21,23]
yes
| ?- print_even_odd(2, 24).
Even: [2,4,6,8,10,12,14,16,18,20,22,24]
Odd: [3,5,7,9,11,13,15,17,19,21,23]
yes
| ?- print_even_odd(1, 35).
Even: [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34]
Odd: [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35]
yes
【讨论】:
library(yall)。使用库谓词没有错,包括元谓词和 lambda 表达式。它们通常会产生紧凑的解决方案。但与避免它们的解决方案相比,它们也可能效率低下,即使更冗长。这里就是这种情况。从学习的角度来看,迄今为止提出的所有不同解决方案都提供了丰富的体验。这使他们所有人都值得。
You mean library(yall). 是的。谢谢指正。
有几种可能的解决方案。这里a,天真,没有使用高阶谓词:
print_even_odd(Lower,Upper,[],[]):-
Lower > Upper, !.
print_even_odd(Lower,Upper,Lodd,Leven):-
Res is Lower mod 2,
( Res =:= 0 ->
Leven = [Lower | LE1], LO1 = Lodd ;
Lodd = [Lower | LO1], LE1 = Leven
),
L1 is Lower + 1,
print_even_odd(L1,Upper,LO1,LE1).
?- print_even_odd(1,15,LE,LO).
LE = [1, 3, 5, 7, 9, 11, 13, 15],
LO = [2, 4, 6, 8, 10, 12, 14]
您可以使用 cmets 中建议的高阶谓词编写更紧凑的解决方案。
【讨论】:
Leven = [Lower|Le1] 替换append([Lower],LE1,Leven)。 (其他 append 也一样)
append/3 谓词的这种冗余使用。
append/3,然后在评论中看到该程序可以改进更有指导意义,因为append/3 经常被过度使用(如在我的第一个代码)
这是另一种可能的解决方案:
% range(+Start, +Stop, +Step, -List)
range(Start, Stop, Step, List) :-
( Start > Stop
-> List = []
; List = [Start|Rest],
Next is Start + Step,
range(Next, Stop, Step, Rest) ).
% even_odd_lists(+Lower, +Upper, -EList, -OList)
even_odd_lists(Lower, Upper, EList, OList) :-
FirstEven is Lower + Lower mod 2,
FirstOdd is Lower + (Lower-1) mod 2,
range(FirstEven, Upper, 2, EList),
range(FirstOdd, Upper, 2, OList).
一些示例查询是:
?- range(1, 10, 3, R).
R = [1, 4, 7, 10].
?- range(1, 10, 2, R).
R = [1, 3, 5, 7, 9].
?- range(2, 10, 2, R).
R = [2, 4, 6, 8, 10].
?- even_odd_lists(1, 15, LE, LO).
LE = [2, 4, 6, 8, 10, 12, 14],
LO = [1, 3, 5, 7, 9, 11, 13, 15].
?- even_odd_lists(2, 15, LE, LO).
LE = [2, 4, 6, 8, 10, 12, 14],
LO = [3, 5, 7, 9, 11, 13, 15].
【讨论】: