【问题标题】:Prolog converting integer to a list of digitProlog将整数转换为数字列表
【发布时间】:2013-04-16 21:22:01
【问题描述】:

我想写一个谓词,一个整数和一个数字列表,如果 Digits 以正确的顺序包含整数的数字,则成功,即:

?-digit_lists( Num, [1,2,3,4] ).
[Num == 1234].

这是我目前所拥有的:

my_digits( 0, [] ).
my_digits(N,[A|As]) :- N1 is floor(N/10), A is N mod 10, my_digits(N1, As).

【问题讨论】:

  • 你有没有尝试过?在不尝试任何事情的情况下寻求家庭作业的帮助是不好的形式。
  • 在调用 my_digits/2 之前尝试反转列表,因为这样您的逻辑将适用...

标签: list prolog digit clpfd


【解决方案1】:

如前所述,考虑使用有限域约束:

:- use_module(library(clpfd)).

number_digits(Number, 0, [Number]) :- Number in 0..9.
number_digits(Number, N, [Digit|Digits]) :-
        Digit in 0..9,
        N #= N1 + 1,
        Number #= Digit*10^N + Number1,
        Number1 #>= 0,
        N #> 0,
        number_digits(Number1, N1, Digits).

这个谓词可以在各个方向使用。实例化任一参数的示例:

?- number_digits(215, _, Ds).
Ds = [2, 1, 5] ;
false.

?- number_digits(N, _, [4,3,2,1]).
N = 4321 ;
false.

还有两个更一般的查询:

?- number_digits(N, _, [A,B]).
N in 10..99,
_G2018+B#=N,
_G2018 in 10..90,
A*10#=_G2018,
A in 0..9,
B in 0..9 ;
false.

?- number_digits(N, _, Ds).
Ds = [N],
N in 0..9 ;
Ds = [_G843, _G846],
N in 0..99,
_G870+_G846#=N,
_G870 in 0..90,
_G843*10#=_G870,
_G843 in 0..9,
_G846 in 0..9 ;
etc.

【讨论】:

  • Digit in 1..9 是错字吗?我期待的是Digit in 0..9
  • 是的,这是一个错字。谢谢!
【解决方案2】:

我认为这更容易:

numToList(NUM,[LIST|[]]):-
   NUM < 10,
   LIST is NUM,
   !.
numToList(NUM,LIST):-
   P is NUM // 10,
   numToList(P,LIST1),
   END is (NUM mod 10), 
   append(LIST1,[END] ,LIST).

【讨论】:

  • 虽然我喜欢这种方法的简单性,但应该注意的是,不幸的是它不能双向工作(给它一个列表不会建立数字),这是我们应该做的在 Prolog 中争取,
【解决方案3】:

还有一个基于的变体...基于(#=)/3 if_//3 我们定义:

n_base_digits(N, R, Ds) :- N #> 0, % 仅正整数 R #> 1, % 最小基数 = 2 Ds = [D|_], % 前导数不能为 0 D#>0, 短语(n_base_digits_aux(N,R,Ds),Ds)。 n_base_digits_aux(N, Base, [_|Rs]) --> { D #= N mod Base, M #= N // 基数 }, if_(M #= 0, { Rs = [] }, n_base_digits_aux(M, Base, Rs)), [D]。

使用 SICStus Prolog 4.3.3 查询:

| ?- n_base_digits(1234, 10, Ds).
Ds = [1,2,3,4] ? ;
no

反之亦然!

| ?- n_base_digits(I,10,[1,2,3]).
I = 123 ? ;
no

请注意,上述方法比建议的by @mat in his answer 中的number_digits/3 更快。

【讨论】:

    【解决方案4】:

    您还可以避免递归并使用内置谓词进行类型转换:

    my_digits(Number, List) :-
        atomic_list_concat(List, Atom),
        atom_number(Atom, Number).
    

    第一行将列表转换为原子,第二行将该原子转换为数字,如果该数字与传入的相同,则返回 true。

    不知道有没有更直接的方法可以将列表转成数字(不这么认为..),这样的话单行就可以实现。

    【讨论】:

      【解决方案5】:

      我不同意@ssBarBee。毕竟,如果您提供您的清单并且他们的指控是正确的,您应该得到 4321;但是你得到了这个:

      ?- my_digits(Num, [1,2,3,4]).
      ERROR: is/2: Arguments are not sufficiently instantiated
      

      我们可以试试clpfd:

      my_digits( 0, [] ).
      my_digits(N,[A|As]) :- N1 #= N/10, A #= N mod 10, my_digits(N1, As).
      

      我们得到这个:

      ?- my_digits(Num, [1,2,3,4]), label([Num]).
      Num = -6789 ;
      Num = 4321.
      

      我觉得这一切都很好奇,但是使用 clpfd 进行跟踪并不愉快。

      如果你只是想解析一个数字列表,我倾向于让它像这样尾递归:

      my_digits(Num, List) :- my_digits(0, List, Num).
      
      my_digits(Num, [], Num).
      my_digits(N, [A|As], Num) :- N1 is N * 10 + A, my_digits(N1, As, Num).
      

      这给了我们:

      ?- my_digits(Num, [1,2,3,4]).
      Num = 1234 ;
      false.
      

      但它不会生成:

      ?- my_digits(1234, X).
      ERROR: is/2: Arguments are not sufficiently instantiated
      

      如果我在没有 clpfd 的情况下解决这个问题,我会倾向于只检查我的论点并使用单独的谓词。格罗斯,我知道,但这就是我会做的。

      my_digits(Num, List) :- 
          nonvar(List), 
          my_digits_p(0, List, Num).
      my_digits(Num, List) :- 
          var(List), 
          my_digits_g(Num, ListRev), 
          reverse(ListRev, List).
      
      my_digits_p(Num, [], Num).
      my_digits_p(N, [A|As], Num) :- N1 is N * 10 + A, my_digits(N1, As, Num).
      
      my_digits_g(0, []) :- !.
      my_digits_g(N, [A|As]) :- A is N mod 10, N1 is floor(N / 10), my_digits_g(N1, As).
      

      这可以解析或检查,或生成数字是否为非变量:

      ?- my_digits(1234, X).
      X = [1, 2, 3, 4].
      
      ?- my_digits(X, [1,2,3,4]).
      X = 1234 ;
      false.
      
      ?- my_digits(1234, [1,2,3,4]).
      true;
      false.
      

      如果你尝试将两个参数都作为变量生成,你会得到一个非常无用的结果:

      ?- my_digits(X, Y).
      X = 0,
      Y = [].
      

      所以我们可以尝试通过向 my_digits 添加另一个特殊情况来生成:

      my_digits(Num, List) :- 
          var(Num), var(List), 
          my_digits_g_from(0, Num, ListRev), 
          reverse(ListRev, List).
      my_digits(Num, List) :- 
          nonvar(List), 
          my_digits_p(0, List, Num).
      my_digits(Num, List) :- 
          var(List), 
          my_digits_g(Num, ListRev),
          reverse(ListRev, List).
      
      my_digits_g_from(N, N, List)   :- my_digits_g(N, List).
      my_digits_g_from(N, Num, List) :- succ(N, N1), my_digits_g_from(N1, Num, List).
      

      这是很多代码,并且很好地演示了不使用 clp(fd) 时必须做的那种杂技。不幸的事实是,在 Prolog 中进行算术运算时,必须解决 is 不统一这一事实,但 clp(fd) 的复杂性很好地证明了这一点。

      希望其他人有更优雅的解决方案!

      【讨论】:

      • 我同意丹尼尔。我匆忙发表评论,导致评论不准确。为您在上述代码中投​​入的工作和时间 +1。
      • 这发生在我们所有人身上。谢谢!
      • 这会产生一个实例化错误,例如查询?- my_digits(N, [_]).
      • 是的,它并不完美。就像我提到的,这更多地证明了在clp(fd) 之外处理算术是多么困难。如果您看到一种简单的解决方法,我很想知道。
      • 简单:查看我发布的 clp(fd) 版本。如果连你自己都觉得恶心,为什么还要用低级算术呢?您传达的印象是您的最终版本是一个完成的替代方案,我只想明确指出,尽管您做出了努力(我很钦佩),但它仍然不是。
      【解决方案6】:

      为了课堂作业?教授可能正在寻找的是以下内容。一般规则,您对问题陈述的分析应首先确定特殊情况(在这种情况下,为零和负值),然后是一般情况。

      : -- int_2_digits/2 ------------------------------------------------------------
      : 
      : The public api.
      :
      : we've got 2 special cases here:
      : 
      : * zero, and
      : * negative numbers
      :
      : and, of course, the general case: a positive value.
      :
      : ------------------------------------------------------------------------------
      int_2_digits( 0 , [0] ) .       : zero is a special case
      int_2 digits( X , ['-'|Ds] ) :- : negative numbers are a special case
        X < 0 ,                       :   which we handle (YMMV) by prepending the
        X1 is - X ,                   :   sign and than processing the absolute value
        int_2_digits(X1,Ds) .         :
      int_2_digits( X , Ds       ) :- : the general case is a positive value
        X > 0 ,                       : just invoke the worker predicate.
        int_2_digits(X,[],Ds) .       :
      
      : -- int_2_digits/3 ------------------------------------------------------------
      : 
      : The guts of the operation.
      : 
      : We're using an accumulator here because we compute the result right-to-left,
      : from least significant digit to most significant digit. Using the accumulator
      : builds the list in the correst sequence, so we don't have to reverse it at
      : the end.
      : ------------------------------------------------------------------------------
      int_2_digits( 0 , Ds , Ds ) .      : if we hit zero, we're done. Unify the accumulator with the result
      int_2_digits( X , Ts  , Ds ) :-    : otherwise...
        D is mod(X,10) ,                 : - get the current digit (X modulo 10)
        T is div(X,10) ,                 : - get the next value via integer division
        int_2_digits( X1 , [T|Ts] , Ds ) : - recurse down
        .                                : Easy!
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-21
        • 2012-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-17
        相关资源
        最近更新 更多