In Prolog, there are predicates that are bidirectional (or even "multidirectional"), in the sense that one can split the set of variables into input and output variables in many different ways. For example, the predicate number_string
acts like a bijection in both directions:
number_string(N, "42"). % finds solution N = 42
number_string(42, S). % finds solution S = "42"
However, this wonderful property does not seem to be preserved by the conjunction of clauses. For example, consider the following two predicates, which simply translate strings like 3x^2
into pieces of an abstract syntax tree like term(3,2)
:
parse_stuff(String, term(Coeff, Pow)) :-
string_concat(CoeffStr, MonomialStr, String),
string_concat("x^", PowStr, MonomialStr),
number_string(Coeff, CoeffStr),
number_string(Pow, PowStr).
write_stuff(String, term(Coeff, Pow)) :-
number_string(Pow, PowStr),
number_string(Coeff, CoeffStr),
string_concat("x^", PowStr, MonomialStr),
string_concat(CoeffStr, MonomialStr, String).
Both predicates are exactly the same, except that the order of the defining clauses on the right hand side is reversed. All clauses on the right hand side are themselves bidirectional.
Here is an example session:
?- parse_stuff("3x^2", X).
X = term(3, 2).
?- parse_stuff(X, term(3, 2)).
ERROR: string_concat/3: Arguments are not sufficiently instantiated
?- write_stuff(X, term(3, 2)).
X = "3x^2".
?- write_stuff("3x^2", X).
ERROR: number_string/2: Arguments are not sufficiently instantiated
Both predicates work only in one way, although they both are obviously bijections, which is pretty sad.
The question consists of two parts (there are two requirements: easy to use, easy to maintain):
- Is there any robust way to build bidirectional predicates in Prolog?
- Is it possible to avoid code-duplication (that is, maintaining two versions: one normal, one reversed)?
Maybe there is some flag that tells "reverse the order of clauses if necessary"?
I'm currently using SWI-Prolog 7.1.x, if it's relevant.
last([ |R],E) :- last(R,E). last([E],E).
, which results in an infinite loop forlast(L, 1)
, but not forlast([1], E)
. – Niklas Peter