2015-06-06 9 views
5

Sto costruendo un parser e un generatore per date e orari. In un normale linguaggio di programmazione questi sarebbero scritti separatamente. In Prolog + CLP (FD) posso scrivere un predicato che fa entrambi :-)Vincolo non propagato all'istanziazione dei membri dell'elenco

Nel mio caso di utilizzo ha spesso senso analizzare un numero di cifre e convertirlo in un numero intero o generare un numero di cifre basato su un dato numero intero.

Il mio problema è che clpfd:run_propagator/2 non viene chiamato quando le singole cifre vengono istanziate, nonostante le mie dichiarazioni che utilizzano clpfd:init_propagator/2. C'è un modo per fare questo o sto commettendo un errore nella mia definizione di clpfd_digits/2?

codice implementato in SWI-Prolog:

:- use_module(library(apply)). 
:- use_module(library(clpfd)). 

:- multifile(clpfd:run_propagator/2). 

day(D) --> {clpfd_digits(D, [D1,D2])}, digit(D1), digit(D2). 

digit(D) --> [C], {code_type(C, digit(D))}. 

clpfd_digits(N, Ds):- 
    clpfd:make_propagator(clpfd_digits(N, Ds), Prop), 
    clpfd:init_propagator(N, Prop), 
    clpfd:init_propagator(Ds, Prop), 
    forall(
    member(D, Ds), 
    clpfd:init_propagator(D, Prop) 
), 
    clpfd:trigger_once(Prop). 

clpfd:run_propagator(clpfd_digits(N, Ds), MState):- 
    ( maplist(is_digit0, Ds) 
    -> clpfd:kill(MState), 
     digits_to_nonneg(Ds, N) 
    ; integer(N) 
    -> clpfd:kill(MState), 
     nonneg_to_digits(N, Ds) 
    ; true 
). 

digits_to_nonneg([], 0):- !. 
digits_to_nonneg(Ds, N):- 
    maplist(char_weight, Chars, Ds), 
    number_chars(N, Chars). 

char_weight(Char, D):- 
    char_type(Char, digit(D)). 

nonneg_to_digits(0, []):- !. 
nonneg_to_digits(N, Ds):- 
    atom_chars(N, Chars), 
    maplist(char_weight, Chars, Ds). 

is_digit0(D):- integer(D), between(0, 9, D). 

Esempio di utilizzo:

?- string_codes("12", Cs), phrase(day(D), Cs). 
Cs = [49, 50], 
clpfd_digits(D, [1, 2]). 

Come si può vedere il vincolo non viene calcolato per ricavare il valore di D.

+2

Vedere [questa risposta] (http://stackoverflow.com/a/28442760/772868) per il modo clpfd per risolvere un problema correlato. – false

+1

Cosa ti aspetti di guadagnare rispetto a 'when ((ground (Codes); nonvar (N)), number_codes (N, Codes))'? Entrambi sono deboli e non le relazioni - del resto. – false

+0

@false Questo è abbastanza vicino! Tuttavia non tratta gli zeri di riempimento, ad esempio 'frase (giorno (2)," 02 ")'. –

risposta

4

+1 per l'utilizzo dei vincoli CLP (FD) per questa attività!

forall/2 ei vincoli non si combinano molto bene, poiché il backtracking revoca i vincoli pubblicati.

Il vostro esempio funziona come previsto con:

flip_init(Prop, D) :- clpfd:init_propagator(D, Prop).

e utilizzando maplist(flip_init(Prop), Ds) invece di forall/2.

Il prossimo problema è quindi che digits_to_nonneg([1,2], N) fallisce semplicemente, ma questo non è correlato all'attuale innesco del vincolo, che avviene come previsto. (A proposito: Usando i vincoli, si può essere in grado di semplificare il codice in modo che è possibile utilizzare un singolo predicato in entrambe le direzioni.)

Inoltre, è possibile utilizzare al posto di in/2between/3: D in 0..9. Questo è spesso utile se si vuole usarlo come un vincolo invece di un semplice test.

+0

Qualcuno dei tre uptotes ha effettivamente ** testato ** il codice risultante? Ci sono ancora molti altri errori in esso. Come i due tagli. – false

+0

Io certamente non l'ho fatto: dopo aver risposto alla domanda vera (* facendo scattare * il propagatore), non ho nemmeno tentato di correggere il primo errore che ho trovato dopo ('digits_to_nonneg/2' non riuscendo inaspettatamente), molto meno ancora oltre. Scrivere dei propagatori corretti vale certamente molte domande a sé stante. – mat

Problemi correlati