Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog: append number to a term

Is it possible to append a number to a term directly?

I.e., I can easily do something like this:

?- A = 1 + 2, B = 3, C = A + B.
C = 1+2+3

But is there a way (operator?) to specify something instead of '+' in the C = A + B to get "C = 1+23"?

I feel I'm asking for something strange, so here is the context. I have a list of digits, and I want to generate all expressions that can be obtained by putting '+', '-' or nothing between the digits.

Pluses and minuses are easy part:

possible([X], X) :- !.
possible([A, B | Rest], E) :-
    ( H = A + B ; H = A - B ),
    possible([H | Rest], E).

?- possible([1, 2, 3], E).
E = 1+2+3 ?;
E = 1+2-3 ?;
E = 1-2+3 ?;
E = 1-2-3
yes

But I also want to get "E = 12+3", "E = 1+23" and "E = 123". Is there an easy way to do it?

Update: the solution should be portable or at least work in B-Prolog.

like image 579
Sergii Dymchenko Avatar asked Mar 19 '23 16:03

Sergii Dymchenko


1 Answers

here is my bet

possible([N|Ns], D) :-
    digits_number([N|Ns], D).
possible([N|Ns], X) :-
    append([L|Ls], [R|Rs], [N|Ns]),
    possible([L|Ls], Lx),
    digits_number([R|Rs], Rx),
    (Op = + ; Op = -), X =.. [Op, Lx, Rx].

digits_number(Digits, N) :-
    maplist(digit_code, Digits, Codes),
    number_codes(N, Codes).
digit_code(D, C) :-
    C is D + 0'0.

The purpose of the verbose [N|Ns], etc is to avoid matching empty lists.

edit Here is a variation, that doesn't require maplist/3 and number_codes/2. The code it's quite similar in size...

possible(Ns, D) :-
    digits_number(Ns, _, D).
possible([N|Ns], X) :-
    append([L|Ls], [R|Rs], [N|Ns]),
    possible([L|Ls], Lx),
    digits_number([R|Rs], _, Rx),
    (Op = + ; Op = -), X =.. [Op, Lx,Rx].

digits_number([Digit], 1, Digit).
digits_number([D|Ds], F, N) :-
    digits_number(Ds, G, T),
    F is G * 10,
    N is T + D * F.

It's more efficient tough (at least on inference count), indeed here is a performance test

?- L=[1,2,3,4,5,6,7,8], time(findall(X,possible_1(L,X),L1)), time(findall(X,possible_2(L,X),L2)).
% 31,591 inferences, 0.017 CPU in 0.017 seconds (100% CPU, 1851600 Lips)
% 20,656 inferences, 0.017 CPU in 0.018 seconds (98% CPU, 1192235 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8],
L1 = L2, L2 = [12345678, 1+2345678, 1-2345678, 12+345678, 12-345678, 1+2+345678, 1+2-345678, ... - ... + 345678, ... - ...|...].

Of course, I've renamed the two versions possible_1, possible_2

like image 177
CapelliC Avatar answered Apr 02 '23 10:04

CapelliC