This is probably something that people have needed to solve before.
How do I solve this elegantly?
I would use Prolog, because it fits perfectly in this case.
Prolog predicates are made of different clauses; let's define
x/1 predicate (
x/1 means functor
x with arity 1, a.k.a. the number of arguments) :
We defined 3 alternative clauses for
x/1 (order of declaration matters).
Then, any call to
V a free variable will leave "choice points" that are visited upon backtrack. Interactively:
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)
We do not necessarily need to have two clauses, though:
y(Y) :- Y = "hello"; Y = "goodbye".
; disjunction operator separates two alternative unifications of variable
V with different strings*.
Let's define also a
z/1 predicate, using the between/4 built-in auxiliary predicate :
z(Result) :- between(0,100,1,Result).
Now, you will need to call a specific test function, which depends heavily on your exact requirements. But here is a sketch of how to call it:
% failing here will backtrack over other values of X, Y, Z.
% 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."
The control flow of the program is lead by an implicit backtracking mechanism: basically, in order to see if
run/0 succeeds, we try both clauses, one after the other.
In order for the the first clause to succeed, all the goals that are listed must succeed. Goals
z(Z) bind one of the possible values to free variables X, Y and Z. When we reach the
fail predicate, which always fails, we must try alternative valuations of the free variables; first, all values for Z are tested, then another for Y and again, all values of Z, until we try all combinations of X, Y and Z. In fact, the first clause of
run/0 can never succeed (but we try anyway, and as a side-effect we call the test with all combination of values). Finally, we end-up trying the other clause of
run/0, which trivially succeeds.
test/3 predicate is where you should define your test. You might want to concatenate all your terms and call an external shell, for example:
Alernatively, you can talk to another process with sockets or through a stream. This particular example does not handle possible spaces in arguments, so take care.
@Iserni pointed out that you might want to avoid doing an exhaustive search. If you think you need to cut down your search tree according to additional constraints, then you can encode your constraints and cost functions as predicates: this is exactly the kind of problems people solve daily with Prolog.
It isn't python or any other popular scripting language, but I think it is worth trying. After all, if it fails, you can just got back trying another option :-)
(*) Even though the resulting code is short, it could be shorter using only the
member/2 predicate for X and Y (