List Prolog将整数转换为数字列表

List Prolog将整数转换为数字列表,list,prolog,digit,clpfd,List,Prolog,Digit,Clpfd,我想写一个谓词,表示一个整数和一个数字列表,如果数字以正确的顺序包含整数的数字,则成功,即: ?-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). 我不同意@ssBarBee。毕竟,如果你提供了你的名单并且他们的指控是正确的,你应该得

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

?-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).

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

?- my_digits(Num, [1,2,3,4]).
ERROR: is/2: Arguments are not sufficiently instantiated
?- my_digits(Num, [1,2,3,4]), label([Num]).
Num = -6789 ;
Num = 4321.
我们可以用
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]).
ERROR: is/2: Arguments are not sufficiently instantiated
?- 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_数字中添加另一个特殊情况来生成:

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)
的复杂性很好地证明了这一点


我希望其他人有一个更优雅的解决方案

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

my_digits(Number, List) :-
    atomic_list_concat(List, Atom),
    atom_number(Atom, Number).
第一行将列表转换为一个原子,第二行将该原子转换为一个数字,如果该数字与传入的数字相同,则该数字将为true


< >我不知道是否有更直接的方式将列表转换成一个数字(不这样认为……),在这种情况下,它可以在一行中实现。

< P>如已经建议的,考虑使用有限域约束:

:- 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.

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

: -- 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!
:--整数2位/2------------------------------------------------------------
: 
:公共api。
:
:我们这里有两种特殊情况:
: 
:*零,以及
:*负数
:
:当然,还有一般情况:正值。
:
: ------------------------------------------------------------------------------
整数2位(0,[0]):零是一个特例
int|2位(X,['-'| Ds]):-:负数是一种特殊情况
X<0:我们通过在
X1为-X,:符号,然后处理绝对值
整数2位(X1,Ds):
int_2_位(X,Ds):-:一般情况下为正值
X>0,:只需调用worker谓词。
整数2位(X,[],Ds):
:--整数2位/3------------------------------------------------------------
: 
:手术的勇气。
: 
:我们在这里使用累加器,因为我们从右向左计算结果,
:从最低有效位到最高有效位。使用蓄能器
:以正确的顺序生成列表,因此我们不必在
:结束。
: ------------------------------------------------------------------------------
整数2位(0,Ds,Ds):如果我们达到零,我们就完了。将累加器与结果统一
整数2位(X,Ts,Ds):-:否则。。。
D是模(X,10):-获取当前数字(X模10)
T是div(X,10):-通过整数除法获得下一个值
整数2位(X1[T|Ts],Ds):-向下递归
.                                : 容易的!

这是另一个基于。。。基于
(#=)/3
,我们定义:

n_base_digits(N, R, Ds) :- N #> 0, % positive integers only R #> 1, % smallest base = 2 Ds = [D|_], % leading digit may not be 0 D #> 0, phrase(n_base_digits_aux(N, R, Ds), Ds). n_base_digits_aux(N, Base, [_|Rs]) --> { D #= N mod Base, M #= N // Base }, if_(M #= 0, { Rs = [] }, n_base_digits_aux(M, Base, Rs)), [D]. 反之亦然

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

请注意,上述数字比建议的数字/3快。

我认为这更容易:

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).
numToList(NUM,[LIST |[]]):-
NUM<10,
列表是NUM,
!.
numToList(NUM,LIST):-
P是NUM//10,
numToList(P,列表1),
结束是(数值mod 10),
追加(列表1,[END],列表)。

您试过什么了吗?不做任何尝试就要求帮助做作业不是一种好的形式。在调用我的数字/2之前,尝试颠倒列表,因为这样你的逻辑就会适用……我同意丹尼尔。我匆忙发表了一个评论,这导致了一个不准确的评论+感谢您在上述代码中投入的工作和时间。我们每个人都会遇到这种情况。谢谢这会产生一个实例化错误,例如查询
?-my_digits(N,[[u])。
是的,它不是完美的。正如我所提到的,这更多地说明了在
clp(fd)
之外处理算术是多么困难。如果你看到一个简单的方法来解决这个问题,我想知道。简单:看看我发布的clp(fd)版本。如果连你自己都觉得恶心,为什么还要用低级算术呢?你传达了这样一种印象,即你的最终版本是一个完成的替代品,我只想明确指出,尽管你做出了努力(我很钦佩),但它仍然不是。1..9中的
数字是一个打字错误吗?我原以为是0..9中的
数字。是的,是打字错误。谢谢虽然我非常喜欢这种简单的方法,但值得注意的是,这种方法并不是双向的(给它一个列表并不能建立数字),这是我们应该在Prolog中努力实现的,