2013-04-16 45 views
3

列表我想写一个谓词,整数和数字的列表,并取得成功,如果位数包含在正确的顺序整数的数字,即:Prolog的转换整数位

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

您是否尝试过什么了吗?在没有任何尝试的情况下寻求家庭作业的帮助是不好的形式。 –

+0

尝试在调用my_digits/2之前颠倒列表,因为那么你的逻辑将适用... – ssBarBee

回答

0

我不同意@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,我会在这一点上倾向于只检查我的ARG并有单独的谓词。格罗斯,我知道,但那就是我要做的。

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

我希望别人有更优雅的解决方案!

+0

我同意丹尼尔。我冲了一下评论,这导致了一个不准确的评论。 +1为您投入上述代码的工作和时间。 – ssBarBee

+1

它发生在我们所有人身上。谢谢! –

+0

这会在查询'? - my_digits(N,[_])上产生实例化错误。' – mat

1

您也可避免递归,并使用内置谓词类型转换:

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

第一行列表转换为原子,而第二线将这个原子的数量,这将给如果该数字与传入的数字相同,则为true。

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

3

前面已经提出,可以考虑使用有限域约束:

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

“数字在1..9”是否是一个错字?我期待'0..9'的数字代替。 –

+1

是的,这是一个错字。谢谢! – mat

0

对于课堂作业?教授可能在寻找的东西如下所示。一般来说,你对问题陈述的分析首先应该确定特殊情况(在这种情况下是零值和负值),然后是一般情况。

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

这里来又基于另一种变体...基于(#=)/3if_//3我们定义:使用SICStus序言4.3.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(1234, 10, Ds). 
Ds = [1,2,3,4] ? ; 
no 

的作品其他方式呢!

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

请注意以上的速度比number_digits/3拟议by @mat in his answer

0

我觉得这是比较容易:

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