Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog unpacking lists predicate

Tags:

prolog

I tried to create something what would work like this:

?- unpacking([[1], [1,2], [3]], Lst1, NewLst).
NewLst=[1,3]

I wrote it like this:

unpacking([], Lst1, Lst1).
unpacking([[H]|T], Lst1, NewLst):-
    append([H], Lst2),
    unpacking(T, Lst2, NewLst).
unpacking([_|T], Lst1, NewLst):-
    unpacking(T, Lst1, NewLst).

and I know that I am doing something wrong. I am starting in Prolog so, need to learn from my mistakes :)

like image 717
Bl4ckCoding Avatar asked May 01 '17 17:05

Bl4ckCoding


2 Answers

Based on @coder's solution, I made my own attempt using if_ and DCGs:

one_element_([], true).
one_element_([_|_],false).

one_element([], false).
one_element([_|Xs], T) :-
    one_element_(Xs, T).

f([]) -->
    [].
f([X|Xs]) -->
    { if_(one_element(X), Y=X, Y=[]) },
    Y,
    f(Xs).

unpack(Xs,Ys) :-
    phrase(f(Xs),Ys).

I only tried for about 30s, but the queries:

?- Xs = [[] | Xs], unpack(Xs,Ys).
?- Xs = [[_] | Xs], unpack(Xs,Ys).
?- Xs = [[_, _ | _] | Xs], unpack(Xs,Ys).

didn't stop with a stack overflow. In my opinion, the critical one should be the last query, but apparently, SWI Prolog manages to optimize:

?- L = [_,_|_], one_element(L,T).
L = [_3162, _3168|_3170],
T = false.

Edit: I improved the solution and gave it a shot with argument indexing. According to the SWI Manual, indexing happens if there is exactly a case distinction between the empty list [] and the non-empty list [_|_]. I rewrote one_element such that it does exactly that and repeated the trick with the auxiliary predicate one_element_. Now that one_element is pure again, we don't lose solutions anymore:

?- unpack([A,B],[]).
A = [_5574, _5580|_5582],
B = [_5628, _5634|_5636] ;
A = [_5574, _5580|_5582],
B = [] ;
A = [],
B = [_5616, _5622|_5624] ;
A = B, B = [].

but

?- unpack([[a,b,c],[a],[b],[c,d]],Items).
Items = [a, b].

is still deterministic. I have not tried this solution in other Prologs, which might be missing the indexing, but it seems for SWI, this is a solution.

Update: Apparently GNU Prolog does not do this kind of indexing and overflows on cyclic lists:

| ?- Xs = [[] | Xs], unpack(Xs,Ys).

Fatal Error: global stack overflow (size: 32770 Kb, reached: 32768 Kb, environment variable used: GLOBALSZ)
like image 106
lambda.xy.x Avatar answered Nov 04 '22 04:11

lambda.xy.x


You probably meant:

unpacking([], []).
unpacking([[E]|T], [E|L]) :-
   unpacking(T, L).
unpacking([[]|T], L) :-
   unpacking(T, L).
unpacking([[_,_|_]|T], L) :-
   unpacking(T, L).

There are more concise ways to write this - and more efficient, too.

like image 38
false Avatar answered Nov 04 '22 04:11

false