This is probably something that people have needed to solve before.
Sì, davvero.
How do I solve this elegantly?
Vorrei usare Prolog, perché si adatta perfettamente in questo caso.
I predicati Prolog sono composti da diverse clausole; definiamo
il predicato x/1
( x/1
significa functor x
con arity 1, a.k.a. il numero di argomenti):
x(-1).
x(0).
x(1).
Abbiamo definito 3 clausole alternative per x/1
(l'ordine di dichiarazione è importante).
Quindi, qualsiasi chiamata a x(V)
con V
una variabile libera lascerà i "punti di scelta" che vengono visitati su backtrack. Interattivo:
[eclipse]: x(V).
V = -1
Yes (0.00s cpu, solution 1, maybe more) ? ;
V = 0
Yes (0.00s cpu, solution 2, maybe more) ? ;
V = 1
Yes (0.00s cpu, solution 3)
Tuttavia, non abbiamo necessariamente bisogno di due clausole:
y(Y) :- Y = "hello"; Y = "goodbye".
Qui, l'operatore di disgiunzione ;
separa due unificazioni alternative della variabile V
con stringhe diverse *.
Definiamo anche un predicato z/1
, usando tra / 4 predicato ausiliario incorporato:
z(Result) :- between(0,100,1,Result).
Ora, dovrai chiamare una funzione di test specifica, che dipende in larga misura dai tuoi requisiti precisi. Ma ecco uno schizzo di come chiamarlo:
run :-
x(X),
y(Y),
z(Z),
test(X,Y,Z),
% failing here will backtrack over other values of X, Y, Z.
fail.
% since the previous clause of the run predicate always fail, we
% add another one that will succeed. It will be tried after all values of
% X, Y and Z have been attempted. Since there is no need to have a body, we
% simply write "run."
run.
Il flusso di controllo del programma è guidato da un meccanismo implicito di backtracking: fondamentalmente, per vedere se run/0
ha esito positivo, proviamo entrambe le clausole, una dopo l'altra.
Affinché la prima clausola abbia successo, tutti gli obiettivi elencati devono avere successo. Obiettivi x(X)
, y(Y)
e z(Z)
vincola uno dei possibili valori per liberare le variabili X, Y e Z. Quando raggiungiamo il predicato fail
, che fallisce sempre, dobbiamo provare le valutazioni alternative delle variabili libere; prima vengono testati tutti i valori per Z, poi un altro per Y e ancora, tutti i valori di Z, finché non proviamo tutte le combinazioni di X, Y e Z. In effetti, la prima clausola di run/0
non può mai avere successo (ma proviamo comunque, e come effetto collaterale chiamiamo il test con tutte le combinazioni di valori). Alla fine, proviamo con l'altra clausola di run/0
, che riesce banalmente.
Il predicato test/3
è il punto in cui dovresti definire il test. Potresti voler concatenare tutti i tuoi termini e chiamare una shell esterna, ad esempio:
test(X,Y,Z) :-
join_string(["./test",X,Y,Z]," ",Cmd),
sh(Cmd).
Alernative, puoi parlare con un altro processo con socket o attraverso un flusso. Questo particolare esempio non gestisce gli spazi possibili negli argomenti, quindi fai attenzione.
@Iserni ha sottolineato che potresti voler evitare una ricerca esauriente. Se pensi di dover abbattere l'albero di ricerca in base a vincoli aggiuntivi, puoi codificare i vincoli e le funzioni di costo come predicati: questo è esattamente il tipo di problemi che le persone risolvono quotidianamente con Prolog.
Non è Python o qualsiasi altro linguaggio di scripting popolare, ma penso che valga la pena provarlo. Dopotutto, se fallisce, puoi semplicemente provare un'altra opzione: -)
(*) Anche se il codice risultante è breve, potrebbe essere più breve utilizzando solo il predicato member/2
per X e Y ( member(X,[-1,0,1])
, member(Y,["hello","goodbye"])
).