【问题标题】:How to write a prolog program to print between a given range?如何编写序言程序以在给定范围之间打印?
【发布时间】: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


【解决方案1】:

这是一种方法:

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/3evens/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 .

【讨论】:

  • 次要观察:“Prolog 有一个内置的 between/3 ...”这是一个事实上的标准谓词(即不是 ISO Prolog 标准谓词),但它并不总是作为内置的谓词,但作为库谓词(例如在 Ciao Prolog、Scryer Prolog、SICStus Prolog 或 XSB 中)。
  • 尽管有特殊的格式和古老的风格,但你是唯一一个使用 between/3 的人,所以你得到了一点。现在您只需要弄清楚如何使用\+ ( Generator, \+ Side_effect ) 作为更清晰的故障驱动循环。
  • 不确定我是否同意你的观点,即这是一个“更清洁”的东西。 Prolog 与其他任何东西一样都是关于模式匹配的。对齐有助于读者进行认知和模式识别。
【解决方案2】:

这是基于 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

【讨论】:

    【解决方案3】:

    这是一个仅使用 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(Lambda)的。现在我不得不怀疑使用那个库是否正确。 (困惑)
    • @GuyCoder 你的意思是library(yall)。使用库谓词没有错,包括元谓词和 lambda 表达式。它们通常会产生紧凑的解决方案。但与避免它们的解决方案相比,它们也可能效率低下,即使更冗长。这里就是这种情况。从学习的角度来看,迄今为止提出的所有不同解决方案都提供了丰富的体验。这使他们所有人都值得。
    • You mean library(yall). 是的。谢谢指正。
    • 本着值得的精神,我给了你的答案和@damianodamiano 的答案。 :)
    【解决方案4】:

    有几种可能的解决方案。这里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 也一样)
    • 当然,我的解决方案非常幼稚
    • @damianodamiano 请编辑您的答案并应用@jnmonette 建议。顺便说一句,像 Logtalk 中的 linter 会警告您注意 append/3 谓词的这种冗余使用。
    • 完成,但我认为将版本保留为append/3,然后在评论中看到该程序可以改进更有指导意义,因为append/3 经常被过度使用(如在我的第一个代码)
    【解决方案5】:

    这是另一种可能的解决方案:

    % 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].
    

    【讨论】:

      猜你喜欢
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-05
      • 1970-01-01
      • 1970-01-01
      • 2020-11-10
      • 1970-01-01
      相关资源
      最近更新 更多