2016-04-21 40 views
5

Supponiamo di voler rappresentare numeri interi come: integer:Sign:[FirstDigit,SecondDigit,...]. Ad esempio, 42 sarebbe rappresentato come integer:positive:[4,2].Elenco di numeri interi e loop infinito in Prolog CLPFD

Ho bisogno di un predicato che generi il valore del numero intero basato su questa rappresentazione e viceversa.

Ecco quello che mi si avvicinò con:

integer_value_('integer':Sign:[H],E) :- 
    H in 0..9, 
    (
     Sign = 'positive', 
     E #= H 
     ; 
     Sign = 'negative', 
     E #= -H 
    ). 
integer_value_('integer':Sign:[H,I|T],E) :- 
    H in 0..9, 
    length([I|T],L), 
    (
     Sign = 'positive', 
     E #= F + H * 10^L 
     ; 
     Sign = 'negative', 
     E #= F - H * 10^L 
    ), 
    integer_value_('integer':Sign:[I|T],F). 

Questo funziona come previsto. Tuttavia, ha la sfortunata proprietà di accettare cose come integer:positive:[0,1], cioè zero iniziali all'inizio della lista. Questo è particolarmente problematico quando enumero tutti i possibili numeri interi usando integer_value_(I,J), label([J]).: anche quelli con zero iniziale vengono visualizzati.

Allora ho tentato di correggere questo usando integer_value_ solo per tutti, ma la prima cifra, e utilizzando integer_value per la prima (tenendo presente che abbiamo bisogno di accogliere per 0 essere rappresentato con una lista che contiene solo 0):

integer_value('integer':Sign:[H],E) :- 
    abs(E) #< 10, 
    abs(E) #> -1, 
    integer_value_('integer':Sign:[H],E). 
integer_value('integer':Sign:[H,I|T],E) :- 
    H in 1..9, 
    length([I|T],L), 
    (
     Sign = 'positive', 
     E #= F + H * 10^L 
     ; 
     Sign = 'negative', 
     E #= F - H * 10^L 
    ), 
    integer_value_('integer':Sign:[I|T],F). 

Tuttavia ora non si comporta correttamente. Ad esempio, integer_value(I,-19). restituisce I = integer:negative:[1, 9], ma se chiediamo un'altra risposta, Prolog entra in un ciclo infinito per motivi che non capisco (dovrebbe dire falso, o già sapere che non ci sono altre risposte).

Questo problema non si verifica con la query "opposta" integer_value(integer:negative:[1,9],Z). che restituisce Z = 19 e quindi false, né si verifica quando entrambi gli argomenti sono variabili (enumera i numeri correttamente, senza zeri iniziali), il che è sorprendente per me.

Qualche idea su cosa si verifichi questo loop infinito e se esiste un modo semplice per risolverlo?

+0

+1 per un caso di utilizzo molto interessante e appropriato dei vincoli CLP (FD)! Ho un piccolo commento riguardo le virgolette singole: puoi omettere il '' 'per tutti gli atomi che non hanno bisogno di virgolette come' positivo', 'negativo',' intero' ecc. Puoi semplicemente scrivere tutti questi atomi direttamente , come 'Segno = positivo',' Segno = negativo' e 'intero: Segno: [I | T]'. – mat

+0

@mat Lo so, ma dal momento che non sono un programmatore Prolog trovo abbastanza brutto avere atomi del genere: p – Fatalize

risposta

5

Per vedere il problema, è sufficiente guardare una piccola parte del programma. Infatti seguente è sufficiente:

 
integer_value('integer':Sign:[H],E) :- false, 
    abs(E) #< 10, 
    abs(E) #> -1, 
    integer_value_('integer':Sign:[H],E). 
integer_value('integer':Sign:[H,I|T],E) :- 
    H in 1..9, 
    length([I|T],L), false, 
    ( Sign = 'positive', 
     E #= F + H * 10^L 
     ; 
     Sign = 'negative', 
     E #= F - H * 10^L 
    ), 
    integer_value_('integer':Sign:[I|T],F). 

L avviene qui per la prima volta, in modo che qualsiasi lunghezza è possibile. Dovrai modificare l'obiettivo di lunghezza in qualche modo.

+1

Nevermind I got it. Ora per risolverlo ... – Fatalize

+2

@Fatalize: vedere [questo] (http://stackoverflow.com/a/28442760/772868) per un problema correlato. – false

3

sono riuscito a risolvere il mio problema utilizzando this other answer sottolineato dalla @false

Uno del pescato è quello di decidere del segno del numero come ultimo passo, in modo che quando l'iterazione attraverso possibili interi otteniamo risposte alternati tra numeri positivi e negativi: dopo aver raggiunto 9 (1 cifra), unificherà con -9, quindi -8, ecc. Dopo -1, si unificherà con 10, 11, ecc. Dopo 99, si unificherà con -99 , -98, ecc. Ottieni il punto.

integer_value('integer':Sign:I,E) :- 
    integer_value('integer':Sign:I,0,E,E). 

integer_value('integer':Sign:[H],N0,N,M) :- 
    H in 0..9, 
    N1 #= H + N0 * 10, 
    abs(M) #>= abs(N1), 
    integer_value_('integer':Sign:[],N1,N,M). 
integer_value('integer':Sign:[H,I|T],N0,N,M) :- 
    H in 1..9, 
    N1 #= H + N0 * 10, 
    abs(M) #>= abs(N1), 
    integer_value_('integer':Sign:[I|T],N1,N,M). 

integer_value_('integer':Sign:[],N0,N,_) :- 
    (
     Sign = 'positive', 
     N #= N0 
     ; 
     Sign = 'negative', 
     N #\= 0, 
     N #= - N0 
    ). 
integer_value_('integer':Sign:[H],N0,N,M) :- 
    H in 0..9, 
    N1 #= H + N0 * 10, 
    abs(M) #>= abs(N1), 
    integer_value_('integer':Sign:[],N1,N,M). 
integer_value_('integer':Sign:[H,I|T],N0,N,M) :- 
    H in 0..9, 
    N1 #= H + N0 * 10, 
    abs(M) #>= abs(N1), 
    integer_value_('integer':Sign:[I|T],N1,N,M).