【问题标题】:How to reverse integer in Prolog using tail-recursion?如何使用尾递归在 Prolog 中反转整数?
【发布时间】:2022-05-22 08:34:29
【问题描述】:

我想在 Prolog 中做一个谓词 reverse(N,Result)。

例如:

  • 反向(12345,结果)。
  • 结果 = 54321。

我必须使用尾递归。我可以使用 *、+、- 和 divmod/4,仅此而已。我不能使用列表。

我可以反转小于 100 的数字,但我不知道如何完成我的代码,我无法完成我的代码以正确反转大于 100 的整数。

reverse(N,N):-
    N <10,
    N>0.

reverse(N,Result):-
    N > 9,
    iter(N,0,Result).

iter(N,Ac,Result):-
    N  < 100, !,
    divmod(N,10,Q,R),
    R1 is R*10,
    Result is Q + R1.

我可以帮忙吗?

提前谢谢你。

【问题讨论】:

  • 我想打招呼,但我无法编辑我的帖子
  • @Yashrod 您是否尝试将数字拆分为数字列表,然后反转列表,然后构建反转的数字?
  • @AntonDanilov 对不起,我应该说我不能使用列表,我编辑我的帖子。
  • 如果N &gt;= 0 而不仅仅是N &gt; 0,为什么reverse(N, N) 不是真的?

标签: recursion prolog integer reverse tail-recursion


【解决方案1】:

我建议使用CLP(FD),因为它提供了整数运算的声明式推理,并且许多 Prolog 系统都提供了它。关于数字反转,我建议您查看条目A004086 in The On-Line Encyclopedia of Integer Sequences。在标题为公式的段落中,您会发现以下公式:

a(n) = d(n,0) with d(n,r) = if n=0 then r else d(floor(n/10),r*10+(n mod 10))

这些可以通过为反转的数字添加一个附加参数来转换为谓词。首先让我们给它起一个漂亮的声明性名称,比如digits_reversed/2。那么关系可以用#&gt;/2#=/2(/)/2+/2mod/2和尾递归来表示:

:- use_module(library(clpfd)).

digits_reversed(N,X) :-
   digits_reversed_(N,X,0).

digits_reversed_(0,R,R).
digits_reversed_(N,X,R) :-
   N #> 0,
   N0 #= N/10,
   R1 #= R*10 + (N mod 10),
   digits_reversed_(N0,X,R1).

注意digits_reversed/2对应a(n)digits_reversed_/3对应d(n,r)上面的公式。现在让我们使用您帖子中的示例查询谓词:

?- digits_reversed(12345,R).
R = 54321 ;
false.

谓词也可以用在另一个方向,即问 什么数被倒转得到 54321? 但是,由于数字的前导零被省略,一个倒转的数有无限多个原始数:

?- digits_reversed(N,54321).
N = 12345 ;
N = 123450 ;
N = 1234500 ;
N = 12345000 ;
N = 123450000 ;
N = 1234500000 ;
N = 12345000000 ;
N = 123450000000 ;
.
.
.

即使是最一般的查询也会产生解决方案,但对于多于一位的数字,您会得到剩余目标作为答案:

?- digits_reversed(N,R).
N = R, R = 0 ;              % <- zero
N = R,
R in 1..9 ;                 % <- other one-digit numbers
N in 10..99,                % <- numbers with two digits
N mod 10#=_G3123,
N/10#=_G3135,
_G3123 in 0..9,
_G3123*10#=_G3159,
_G3159 in 0..90,
_G3159+_G3135#=R,
_G3135 in 1..9,
R in 1..99 ;
N in 100..999,              % <- numbers with three digits
N mod 10#=_G4782,
N/10#=_G4794,
_G4782 in 0..9,
_G4782*10#=_G4818,
_G4818 in 0..90,
_G4818+_G4845#=_G4842,
_G4845 in 0..9,
_G4794 mod 10#=_G4845,
_G4794 in 10..99,
_G4794/10#=_G4890,
_G4890 in 1..9,
_G4916+_G4890#=R,
_G4916 in 0..990,
_G4842*10#=_G4916,
_G4842 in 0..99,
R in 1..999 ;
.
.
.

要通过上述查询获得实际数字,您必须限制N 的范围并在谓词发布算术约束后对其进行标记:

?- N in 10..20, digits_reversed(N,R), label([N]).
N = 10,
R = 1 ;
N = R, R = 11 ;
N = 12,
R = 21 ;
N = 13,
R = 31 ;
N = 14,
R = 41 ;
N = 15,
R = 51 ;
N = 16,
R = 61 ;
N = 17,
R = 71 ;
N = 18,
R = 81 ;
N = 19,
R = 91 ;
N = 20,
R = 2 ;
false.

【讨论】:

  • 感谢您的宝贵时间。这对我帮助很大。
【解决方案2】:

如果由于某种原因您不想要基于约束的解决方案,或者如果您使用的 Prolog 系统不支持约束,则另一种解决方案是:

reverse_digits(N, M) :-
    (   integer(N) ->
        reverse_digits(N, 0, M)
    ;   integer(M),
        reverse_digits(M, 0, N)
    ).

reverse_digits(0, M, M) :- !.
reverse_digits(N, M0, M) :-
    N > 0,
    R is N div 10,
    M1 is M0 * 10 + N mod 10,
    reverse_digits(R, M1, M).

此解决方案可以与绑定到整数的任一参数一起使用,并且不会留下虚假的选择点:

?- reverse_digits(12345, M).
M = 54321.

?- reverse_digits(N, 12345).
N = 54321.

?- reverse_digits(12345, 54321).
true.

但请注意,与基于约束的解决方案不同,此解决方案不能用作满足关系的整数对的生成器

?- reverse_digits(N, M).
false.

【讨论】:

  • 感谢您的帮助。
【解决方案3】:
reverseNumber(N,R):-reverse_acc(N,0,R).
reverse_acc(0,Acc,Acc).
reverse_acc(N,Acc,R):- C is N mod 10, N1 is N div 10, 
                                     Acc1 is Acc * 10 + C, 
                                     reverse_acc(N1, Acc1,R).

【讨论】:

  • 请记住,Stack Overflow 不仅仅是为了解决眼前的问题,而是为了帮助未来的读者找到类似问题的解决方案,这需要了解底层代码。这对于我们社区的初学者和不熟悉语法的成员来说尤其重要。鉴于此,您能否edit 您的答案包括对您正在做什么的解释以及为什么您认为这是最好的方法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 2017-10-09
  • 1970-01-01
相关资源
最近更新 更多