Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog Beginner: Reverse List only once

Assume I have two arbitrary lists that represent the first two items of a 3-place predicate:

[anna,berta,charlotte],[charles,bob,andy]

I want to match every item in a third list (the third item of the 3-place predicate) as follows:

[[anna,andy],[berta,bob],[charlotte,charles]]

Basically the items get matched in a sequentially reverse fashion. To match the items in a sequential manner, I've devised the following code:

match([],[],[]).
match([A|At],[C|Ct],[[A,C]|Dt]):-match(At,Ct,Dt).

But this would give me the following:

match([anna,berta,charlotte],[charles,bob,andy],X).
X=[[anna,charles],[berta,bob],[charlotte,andy]]

So I need to reverse the second list somehow. So far, I've altered the code as follows:

match([],[],[]).
match([A|At],[C|Ct],[[A,B]|Dt]):-reverse([C|Ct],[B|Bt]),match(At,Bt,Dt).

But this would continually reverse the second list with each pass. The result would look as follows:

match([anna,berta,charlotte],[charles,bob,andy],X).
X=[[anna,andy],[berta,charles],[charlotte,bob]]

Question: How do I reverse the second list only ONCE, so the actual results match the desired ones? Or is my approach fundamentally flawed? I'm new to prolog and am currently stymied by this. Any help would be appreciated.

like image 834
Zerkezhi Avatar asked Nov 24 '15 13:11

Zerkezhi


Video Answer


2 Answers

Do exactly what you say: Reverse the list once, and then use the reversed list.

lists_pairs(Ps1, Ps2, Pairs) :-
    reverse(Ps2, RPs2),
    pairs_keys_values(Pairs, Ps1, RPs2).

You can check out the source code of reverse/2 and pairs_keys_values/3 in any decent Prolog library to see how it is defined.

Sample query and answer:

?- lists_pairs([anna,berta,charlotte], [charles,bob,andy], Ps). 
Ps = [anna-andy, berta-bob, charlotte-charles].

I leave converting such pairs to the non-sensible "pair as list" representation as an exercise.

like image 93
mat Avatar answered Sep 16 '22 22:09

mat


The trick to solving problems that require you to apply a rule only once is to build an auxiliary rule which performs extra steps before and/or after invoking the recursive rule:

match(A, B, R) :- reverse(B, RevB), match_impl(A, RevB, R).

match_impl([], [], []).
match_impl([A|At], [C|Ct], [[A,C]|Dt]) :- match_impl(At, Ct, Dt).

match_impl/3 is your match/3 rule renamed to avoid conflicting with the "top" match/3 rule that includes an auxiliary step.

like image 37
Sergey Kalinichenko Avatar answered Sep 16 '22 22:09

Sergey Kalinichenko