Related
I tried to answer another question (wrongly though) and this led to a question on "difference lists" (or "list differences", which seems a more appropriate name, unless "Escherian Construction" isn't preferred)
We have a fully ground list of elements obj(X,Y) (both X and Y ground). We want to retain only the first obj(X,_) where X hasn't been encountered yet when going through the list front to back. Those "first elements" must appear in order of appearance in the result.
Let's specify the problem through test cases:
% Testing
:- begin_tests(collapse_dl).
test(one) :- collapse_dl([],[]).
test(two) :- collapse_dl([obj(a,b)],
[obj(a,b)]).
test(three) :- collapse_dl([obj(a,b),obj(a,c)],
[obj(a,b)]).
test(four) :- collapse_dl([obj(a,b),obj(a,c),obj(b,j)],
[obj(a,b),obj(b,j)]).
test(five) :- collapse_dl([obj(a,b),obj(a,c),obj(b,j),obj(a,x),obj(b,y)],
[obj(a,b),obj(b,j)]).
:- end_tests(collapse_dl).
rt :- run_tests(collapse_dl).
Now, this is easy to implement using filtering, list prepend and reverse/2, but what about using difference lists and list append?
however, I'm not able to get the seen/2 predicate to work. It checks whether obj(A,_) is already in the difference list. But what's a proper termination for this predicate?
% This is called
collapse_dl([],[]) :- !.
collapse_dl([X|Xs],Out) :-
Dlist = [X|Back]-Back, % create a difflist for the result; X is surely in there (as not yet seen)
collapse_dl(Xs,Dlist,Out). % call helper predicate
% Helper predicate
collapse_dl([],Ldown,Lup):- % end of recursion; bounce proper list back up
Ldown = Lup-[]. % the "back" of the difflist is unified with [], so "front" becomes a real list, and is also Lup
collapse_dl([obj(A,_)|Objs],Ldown,Out) :-
seen(obj(A,_),Ldown), % guard: already seen in Ldown?
!, % then commit
collapse_dl(Objs,Ldown,Out). % move down chain of induction
collapse_dl([obj(A,B)|Objs],Ldown,Out) :-
\+seen(obj(A,_),Ldown), % guard: not yet seen in Ldown?
!, % then commit
Ldown = Front-Back, % decompose difference list
Back = [obj(A,B)|NewTail], % NewTail is fresh! Append via difflist unification magic
collapse_dl(Objs,Front-NewTail,Out). % move down chain of induction; Front has been refined to a longer list
% Membership check in a difference list
seen(obj(A,_),[obj(A,_)|_Objs]-[]) :- !. % Yup, it's in there. Cut retry.
seen(Obj,[_|Objs]-[]) :- ... % But now???
Update
With Paulo's code snippet:
% Membership check in a difference list
seen(Element, List-Back) :-
List \== Back,
List = [Element|_].
seen(Element, List-Back) :-
List \== Back,
List = [_| Tail],
seen(Element, Tail-Back).
So, term equivalence, or dis-equivalence in this case, is the solution!
We now pass all the test.
Try (taken from Logtalk difflist library object):
member(Element, List-Back) :-
List \== Back,
List = [Element|_].
member(Element, List-Back) :-
List \== Back,
List = [_| Tail],
member(Element, Tail-Back).
memberchk/2 should do it. Using the approach from here,
%% collapse_dl( ++Full, -Short )
collapse_dl( [obj(K,V) | A], B ) :-
memberchk( obj(K,X), B ),
( X = V -> true ; true ),
collapse_dl( A, B ).
collapse_dl( [], B ) :-
length( B, _), !.
Doing what (functional) Prolog does best, instantiating an open-ended list in a top-down manner.
Passes the tests included in the question.
Addendum: With printouts
%% collapse_dl( ++Full, -Short )
collapse_dl( [obj(K,V) | A], B ) :-
format("Enter : ~w relatedto ~w\n", [[obj(K,V) | A], B]),
% Necessarily find (find or insert) obj(K, X) (thanks to the
% uninstantiated X) in list B which has an "unobserved" tail:
memberchk( obj(K,X), B ),
% Unify X with V if you can; ignore failure if you can't!
( X = V -> true ; true ),
format("Mid : ~w relatedto ~w\n", [[obj(K,V) | A], B]),
collapse_dl( A, B ),
format("Return: ~w relatedto ~w\n", [[obj(K,V) | A], B]).
collapse_dl( [], B ) :-
format("Termination: From unobserved-tail-list ~w ",[B]),
length(B, _),
format("to ~w (and don't come back!)\n",[B]),
!.
Because of the added printouts this code is no longer tail-recursive. The original is, and so has no "return" in its trace: it just goes forward and stops working right away when the input list is traversed to its end.
See more about the distinction e.g. here.
This "open-ended list" technique is not difference list, but the two are very closely related. And we don't actually need the explicit tail anywhere here, except for the final freezing. So we just do the O(n) length call instead of the explicit O(1) Tail = [] we'd do with difference lists, no biggie.
Of bigger impact is the choice of list instead of e.g. tree data structure. Trees can be open-ended too, just need to use var/1 here and there. Next step is the tree's structure. Top-down open-leaved tree can't be rotated (as all the calls reference the same top node) so its depth will depend on the input's orderedness. To maintain good balance the trees need to be rotated on occasion, hence closed; and we're back to the traditional state-passing code, were each call gets two tree arguments -- the one before update, and the other after it: the
upd(T1, T2), next(T2, T3), more(T3, T4), ...
kind of thing. It ought to be used in real code. There are some libraries that do that.
This answer's code is simplistic, in order to be simple and illustrative.
Since I currently need it, I got a simpler solution. Assuming the
difference list is open, means for the pair List-Back, we have var(Back).
Then we can short cut, only passing List:
member_open(_, List) :- var(List), !, fail.
member_open(Element, [Element|_]).
member_open(Element, [_|List]) :- member_open(Element, List).
If we want to append an element to the List, since for example we didn't find it via member_open/2, we simply make Back = [NewElement|Back2] and continue with Back2.
Here is variables/2 (ISO term_variables/2) written this way, so that it doesn't need reverse/1:
variables(T, L) :-
variables(T, B, B, B2),
B2 = [],
L = B.
variables(V, L, B, B) :- var(V), member_open(W, L), V == W, !.
variables(V, L, [V|B], B) :- var(V), !.
variables(T, L, B, B2) :-
T =.. [_|A],
variables_list(A, L, B, B2).
variables_list([T|A], L, B, B2) :-
variables(T, L, B, H),
variables_list(A, L, H, B2).
variables_list([], _, B, B).
Seems to work:
?- variables(f(X,g(X,Y),Y), L).
L = [X, Y].
My confusion mainly lies around understanding singleton variables.
I want to implement the predicate noDupl/2 in Prolog. This predicate can be used to identify numbers in a list that appear exactly once, i. e., numbers which are no duplicates. The first argument of noDupl is the list to analyze. The
second argument is the list of numbers which are no duplicates, as described below.
As an example, for the list [2, 0, 3, 2, 1] the result [0, 3, 1] is computed (because 2 is a duplicate).
In my implementation I used the predefined member predicate and used an auxiliary predicate called helper.
I'll explain my logic in pseudocode, so you can help me spot where I went wrong.
First off, If the first element is not a member of the rest of the list, add the first element to the new result List (as it's head).
If the first element is a member of T, call the helper method on the rest of the list, the first element H and the new list.
Helper method, if H is found in the tail, return list without H, i. e., Tail.
noDupl([],[]).
noDupl([H|T],L) :-
\+ member(H,T),
noDupl(T,[H|T]).
noDupl([H|T],L) :-
member(H,T),
helper(T,H,L).
helper([],N,[]).
helper([H|T],H,T). %found place of duplicate & return list without it
helper([H|T],N,L) :-
helper(T,N,[H|T1]).%still couldn't locate the place, so add H to the new List as it's not a duplicate
While I'm writing my code, I'm always having trouble with deciding to choose a new variable or use the one defined in the predicate arguments when it comes to free variables specifically.
Thanks.
Warnings about singleton variables are not the actual problem.
Singleton variables are logical variables that occur once in some Prolog clause (fact or rule). Prolog warns you about these variables if they are named like non-singleton variables, i. e., if their name does not start with a _.
This convention helps avoid typos of the nasty kind—typos which do not cause syntax errors but do change the meaning.
Let's build a canonical solution to your problem.
First, forget about CamelCase and pick a proper predicate name that reflects the relational nature of the problem at hand: how about list_uniques/2?
Then, document cases in which you expect the predicate to give one answer, multiple answers or no answer at all. How?
Not as mere text, but as queries.
Start with the most general query:
?- list_uniques(Xs, Ys).
Add some ground queries:
?- list_uniques([], []).
?- list_uniques([1,2,2,1,3,4], [3,4]).
?- list_uniques([a,b,b,a], []).
And add queries containing variables:
?- list_uniques([n,i,x,o,n], Xs).
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
?- list_uniques([A,B], [X,Y]).
?- list_uniques([A,B,C], [D,E]).
?- list_uniques([A,B,C,D], [X]).
Now let's write some code! Based on library(reif) write:
:- use_module(library(reif)).
list_uniques(Xs, Ys) :-
list_past_uniques(Xs, [], Ys).
list_past_uniques([], _, []). % auxiliary predicate
list_past_uniques([X|Xs], Xs0, Ys) :-
if_((memberd_t(X,Xs) ; memberd_t(X,Xs0)),
Ys = Ys0,
Ys = [X|Ys0]),
list_past_uniques(Xs, [X|Xs0], Ys0).
What's going on?
list_uniques/2 is built upon the helper predicate list_past_uniques/3
At any point, list_past_uniques/3 keeps track of:
all items ahead (Xs) and
all items "behind" (Xs0) some item of the original list X.
If X is a member of either list, then Ys skips X—it's not unique!
Otherwise, X is unique and it occurs in Ys (as its list head).
Let's run some of the above queries using SWI-Prolog 8.0.0:
?- list_uniques(Xs, Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_A], Ys = []
; Xs = [_A,_A,_A], Ys = []
...
?- list_uniques([], []).
true.
?- list_uniques([1,2,2,1,3,4], [3,4]).
true.
?- list_uniques([a,b,b,a], []).
true.
?- list_uniques([1,2,2,1,3,4], Xs).
Xs = [3,4].
?- list_uniques([n,i,x,o,n], Xs).
Xs = [i,x,o].
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
Xs = [].
?- list_uniques([A,B], [X,Y]).
A = X, B = Y, dif(Y,X).
?- list_uniques([A,B,C], [D,E]).
false.
?- list_uniques([A,B,C,D], [X]).
A = B, B = C, D = X, dif(X,C)
; A = B, B = D, C = X, dif(X,D)
; A = C, C = D, B = X, dif(D,X)
; A = X, B = C, C = D, dif(D,X)
; false.
Just like my previous answer, the following answer is based on library(reif)—and uses it in a somewhat more idiomatic way.
:- use_module(library(reif)).
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition(=(V), Vs, Equals, Difs),
if_(Equals = [], Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
While this code does not improve upon my previous one regarding efficiency / complexity, it is arguably more readable (fewer arguments in the recursion).
In this solution a slightly modified version of tpartition is used to have more control over what happens when an item passes the condition (or not):
tpartition_p(P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, Xs, RTrue, RFalse) :-
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, RTrue, RFalse).
i_tpartition_p([], _P_2, _OnTrue_5, _OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
call(OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse).
i_tpartition_p([X|Xs], P_2, OnTrue_5, OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
if_( call(P_2, X)
, call(OnTrue_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse)
, call(OnFalse_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse) ),
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, NCurrentTrue, NCurrentFalse, RTrue, RFalse).
InitialTrue/InitialFalse and RTrue/RFalse contains the desired initial and final state, procedures OnTrue_5 and OnFalse_5 manage state transition after testing the condition P_2 on each item and OnEnd_4 manages the last transition.
With the following code for list_uniques/2:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition_p(=(V), on_true, on_false, on_end, false, Difs, Vs, HasDuplicates, []),
if_(=(HasDuplicates), Xs=Xs0, Xs = [V|Xs0]),
list_uniques(Difs, Xs0).
on_true(_, _, Difs, true, Difs).
on_false(X, HasDuplicates, [X|Xs], HasDuplicates, Xs).
on_end(HasDuplicates, Difs, HasDuplicates, Difs).
When the item passes the filter (its a duplicate) we just mark that the list has duplicates and skip the item, otherwise the item is kept for further processing.
This answer goes similar ways as this previous answer by #gusbro.
However, it does not propose a somewhat baroque version of tpartition/4, but instead an augmented, but hopefully leaner, version of tfilter/3 called tfilter_t/4 which can be defined like so:
tfilter_t(C_2, Es, Fs, T) :-
i_tfilter_t(Es, C_2, Fs, T).
i_tfilter_t([], _, [], true).
i_tfilter_t([E|Es], C_2, Fs0, T) :-
if_(call(C_2,E),
( Fs0 = [E|Fs], i_tfilter_t(Es,C_2,Fs,T) ),
( Fs0 = Fs, T = false, tfilter(C_2,Es,Fs) )).
Adapting list_uniques/2 is straightforward:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
if_(tfilter_t(dif(V),Vs,Difs), Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
Save scrollbars. Stay lean! Use filter_t/4.
You have problems already in the first predicate, noDupl/2.
The first clause, noDupl([], []). looks fine.
The second clause is wrong.
noDupl([H|T],L):-
\+member(H,T),
noDupl(T,[H|T]).
What does that really mean I leave as an exercise to you. If you want, however, to add H to the result, you would write it like this:
noDupl([H|T], [H|L]) :-
\+ member(H, T),
noDupl(T, L).
Please look carefully at this and try to understand. The H is added to the result by unifying the result (the second argument in the head) to a list with H as the head and the variable L as the tail. The singleton variable L in your definition is a singleton because there is a mistake in your definition, namely, you do nothing at all with it.
The last clause has a different kind of problem. You try to clean the rest of the list from this one element, but you never return to the original task of getting rid of all duplicates. It could be fixed like this:
noDupl([H|T], L) :-
member(H, T),
helper(T, H, T0),
noDupl(T0, L).
Your helper/3 cleans the rest of the original list from the duplicate, unifying the result with T0, then uses this clean list to continue removing duplicates.
Now on to your helper. The first clause seems fine but has a singleton variable. This is a valid case where you don't want to do anything with this argument, so you "declare" it unused for example like this:
helper([], _, []).
The second clause is problematic because it removes a single occurrence. What should happen if you call:
?- helper([1,2,3,2], 2, L).
The last clause also has a problem. Just because you use different names for two variables, this doesn't make them different. To fix these two clauses, you can for example do:
helper([H|T], H, L) :-
helper(T, H, L).
helper([H|T], X, [H|L]) :-
dif(H, X),
helper(T, X, L).
These are the minimal corrections that will give you an answer when the first argument of noDupl/2 is ground. You could do this check this by renaming noDupl/2 to noDupl_ground/2 and defining noDupl/2 as:
noDupl(L, R) :-
must_be(ground, L),
noDupl_ground(L, R).
Try to see what you get for different queries with the current naive implementation and ask if you have further questions. It is still full of problems, but it really depends on how you will use it and what you want out of the answer.
I have a strange problem that I do not know how to solve.
I have written a predicate that compresses lists by removing repeating items.
So if the input is [a,a,a,a,b,c,c,a,a], output should be [a,b,c,a]. My first code worked, but the item order was wrong. So I add a append/3 goal and it stopped working altogether.
Can't figure out why. I tried to trace and debug but don't know what is wrong.
Here is my code which works but gets the item order wrong:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
p08([H2|T], [H1|O], X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
Here's the newer version, but it does not work at all:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
append(H1, O, N),
p08([H2|T], N, X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
H1 is not a list, that's why append(H1, O, N) fails.
And if you change H1 to [H1] you actually get a solution identical to your first one. In order to really reverse the list in the accumulator you should change the order of the first two arguments: append(O, [H1], N). Also, you should change the first rule with one that matches the empty list p08([], X, X) (without it, the goal p08([], [], Out) fails).
Now, to solve your problem, here is the simplest solution (which is already tail recursive, as #false stated in the comments to this answer, so there is no need for an accumulator)
p([], []). % Rule for empty list
p([Head, Head|Rest], Out):- % Ignore the Head if it unifies with the 2nd element
!,
p([Head|Rest], Out).
p([Head|Tail], [Head|Out]):- % otherwise, Head must be part of the second list
p(Tail, Out).
and if you want one similar to yours (using an accumulator):
p08(List, Out):-p08(List, [], Out).
p08([], Acc, Acc).
p08([Head, Head|Rest], Acc, Out):-
!,
p08([Head|Rest], Acc, Out).
p08([Head|Tail], Acc, Out):-
append(Acc, [Head], Acc2),
p08(Tail, Acc2, Out).
Pure and simple:
list_withoutAdjacentDuplicates([],[]).
list_withoutAdjacentDuplicates([X],[X]).
list_withoutAdjacentDuplicates([X,X|Xs],Ys) :-
list_withoutAdjacentDuplicates([X|Xs],Ys).
list_withoutAdjacentDuplicates([X1,X2|Xs],[X1|Ys]) :-
dif(X1,X2),
list_withoutAdjacentDuplicates([X2|Xs],Ys).
Sample query:
?- list_withoutAdjacentDuplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a] ; % succeeds, but leaves useless choicepoint(s) behind
false
Edit 2015-06-03
The following code is based on if_/3 and reified term equality (=)/3 by #false, which---in combination with first argument indexing---helps us avoid above creation of useless choicepoints.
list_without_adjacent_duplicates([],[]).
list_without_adjacent_duplicates([X|Xs],Ys) :-
list_prev_wo_adj_dups(Xs,X,Ys).
list_prev_wo_adj_dups([],X,[X]).
list_prev_wo_adj_dups([X1|Xs],X0,Ys1) :-
if_(X0 = X1, Ys1 = Ys0, Ys1 = [X0|Ys0]),
list_prev_wo_adj_dups(Xs,X1,Ys0).
Let's see it in action!
?- list_without_adjacent_duplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
In this answer we use meta-predicate foldl/4 and
Prolog lambdas.
:- use_module(library(apply)).
:- use_module(library(lambda)).
We define the logically pure predicatelist_adj_dif/2 based on if_/3 and (=)/3:
list_adj_dif([],[]).
list_adj_dif([X|Xs],Ys) :-
foldl(\E^(E0-Es0)^(E-Es)^if_(E=E0,Es0=Es,Es0=[E0|Es]),Xs,X-Ys,E1-[E1]).
Let's run the query given by the OP!
?- list_adj_dif([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
How about a more general query? Do we get all solutions we expect?
?- list_adj_dif([A,B,C],Xs).
A=B , B=C , Xs = [C]
; A=B , dif(B,C), Xs = [B,C]
; dif(A,B), B=C , Xs = [A,C]
; dif(A,B), dif(B,C), Xs = [A,B,C].
Yes, we do! So... the bottom line is?
Like many times before, the monotone if-then-else construct if_/3 enables us to ...
..., preserve logical-purity, ...
..., prevent the creation of useless choicepoints (in many cases), ...
..., and remain monotone—lest we lose solutions in the name of efficiency.
More easily:
compress([X],[X]).
compress([X,Y|Zs],Ls):-
X = Y,
compress([Y|Zs],Ls).
compress([X,Y|Zs],[X|Ls]):-
X \= Y,
compress([Y|Zs],Ls).
The code works recursevely and it goes deep to the base case, where the list include only one element, and then it comes up, if the found element is equal to the one on his right , such element is not added to the 'Ls' list (list of no duplicates ), otherwise it is.
compr([X1,X1|L1],[X1|L2]) :-
compr([X1|L1],[X1|L2]),
!.
compr([X1|L1],[X1|L2]) :-
compr(L1,L2).
compr([],[]).
I'm new to Prolog and I need to remove duplicates from a list like shown in the example below.
?- remove([a,a,b,c,a,a,b,c,b,a,a],X).
X = [a,b,c,a,b,c,b,a]
If I understand correctly, you need to remove adjacents duplicates. Just copy the elements skipping the first of adjacents. I'll let you to complete the code...
remove([X,X|Rest], [X|Rest1]) :-
!, % this commit avoid the logical alternative you'll code below
.... % recurse to get Rest1 from Rest
remove([X|Rest], [X|Rest1]) :-
% recurse to get Rest1 from Rest
% remember the base case !!
edit as false pointed out, such naive remove/2 is buggy. I've corrected adding a remove/3 predicate, that does the copy accounting for a witness, i.e. with an element lookahead.
remove([X|Xs], [X|Ys]) :-
remove(X, Xs, Ys).
remove(X, [X|Xs], Ys) :-
!, % discard the duplicate
... % recurse to get Ys from Xs, with same witness
remove(_, [X|Xs], [X|Ys]) :-
... % the cut above ensure that here we must change witness when recursing
% as always, remember the base case !!
a List of Lists like
Lists=[ [1,2,3],
[4,5,6],
[7,8,3] ]
and i want to get in this case all vertical lists like
[1,4,7], [2,5,8], [3,6,3]
how to do that? i thought about 2 counters witch work together like two "for to do" repeats.
i need to check with "is_set" if [1,4,7] is a set or [3,6,3] witch of course is not.
like this:
el_at(Llist,Gl,1),
el_at(EList, Llist,1),
globalListVertikalCheck(ListVertikal),
addlist(Elist,ListVertikal,NewListVertikal),
el_at(Llist,Gl,2),
el_at(EList, Llist,2),
globalListVertikalCheck(ListVertikal),
addlist(Elist,ListVertikal,NewListVertikal),
thanks
A list of all vertical lists is known as a transposed matrix.
SWI's library(clpfd) contains such code.
I didn't fully understand the solution you propose, but I have another one. I will try to describe how it works and maybe than you can see what was wrong with your solution and why it didn't work.
Let's consider an example of [[1,2], [3,4]]. The idea is to go through the first sub-list [1,2] and create an incomplete result [[1],[2]], then go through the next one [3,4] and prepend (which is easier than append in Prolog) each item in it to the each sub-list in the result. We will end up with [[3,1], [4,1]]. The sub-lists are then reversed and we have the result [[1,3],[1,4]].
Now the implementation:
The vertical predicate is the core, it goes through the list of lists and the result is step by step accumulated in the Acc varible.
For each of the sublists, the vertical predicate calls the addfirst predicate, which takes each element of that sublist and prepends it to the list in which the previous results were accumulated.
vertical([X|Xs],Result):-
createempty(X, Acc),
vertical([X|Xs], Acc, ReversedResults),
reverseall(ReversedResults, Result).
reverseall([], []).
reverseall([X|Xs], [XReversed|Rest]):-
reverse(X, XReversed),
reverseall(Xs, Rest).
createempty([], []).
createempty([X|Xs], [[]|R]):-createempty(Xs,R).
vertical([], Result, Result).
vertical([X|Xs], Acc, Result):-
addfirst(X, Acc2, Acc),
vertical(Xs, Acc2, Result).
addfirst([], [], []).
addfirst(
[Y|Ys],
[[Y|YVerticalRest]|ResultRest],
[YVerticalRest|VerticalsRest]):-
addfirst(Ys, ResultRest, VerticalsRest).
Here goes a small implementation of transpose:
It works by taking the first element of every sublist. When it finishes, it recursively does the same but now with the next item of each list, and so on.
transpose(M, T):-
transpose(M, [], T).
transpose([], [], []).
transpose([], S, [[]|T]):-
S \= [] ->
(reverse(S, M), transpose(M, [], T)).
transpose([[]|MTail], S, T):-
transpose(MTail, S, T).
transpose([[Item|Tail]|MTail], S, [[Item|NTail]|T]):-
transpose(MTail, [Tail|S], [NTail|T]).
transpose([[]|_],[]) :- !.
transpose(L,[L1|R2]) :-
transpose(L,L2,L1),
transpose(L2,R2).
transpose([],[],[]) :- !.
transpose([[A|R1]|R2],[R1|R3],[A|R4]) :-
transpose(R2,R3,R4).