Consider these modifications to tol33t/2
:
tol33t([], []).
tol33t([Code|Codes], Remainder) :-
translate([Code], Translation), !,
tol33t(Codes, Rest),
append(Translation, Rest, Remainder).
tol33t([Code|Codes], [Code|Remainder]) :-
tol33t(Codes, Remainder).
The first clause is the base case.
The second clause will succeed iff there is a translation for the current Code
via translate/2
, as a list of characters of arbitrary length (Translation
- note you had [Lower]
instead, which restricted results to lists of length 1 only). The cut (!
) after the check for a code translation commits to finding the Rest
of the solution recursively and then appends the Translation
to the front, as the Remainder
to return.
The third clause is executed iff there was no translation for the current Code
(i.e., the call to translate/2
) in the second clause. In this case, no translation for the Code
means we just return it as is and compute the rest.
EDIT:
If you don't have cut (!
), the second and third clauses can be combined to become:
tol33t([Code|Codes], Remainder) :-
tol33t(Codes, Rest),
(translate([Code], Translation) ->
append(Translation, Rest, Remainder)
; Remainder = [Code|Rest]
).
This (unoptimized) version checks, at every Code
in the character list, if there is a translate/2
that suceeds; if so, the Translation
is appended to the Rest
, else the Code
is passed through unchanged. Note that this has the same semantics as the implementation above, in that solutions are commited to (i.e., simulating a cut !
) if the antecedent to ->
(translate/2
) succeeds. Note that the cut in both implementations is strictly necessary; without it, the program will backtrack to find solutions where Code
bindings are not translated where there exists an applicable translate/2
clause.