funlive
funlive

Reputation: 61

How to use foreach in SWI-Prolog

I want to convert a list of pairs to two lists – first containing first elements of the pairs, second containing second elements of the pairs.

E.g.

[['Test1', 'US'], ['Test1', 'France'], ['Test2', 'German']]

should produce

List1 = ['Test1', 'Test1', 'Test2']
List2 = ['US', 'France', 'German']

I tried to use foreach and come up with this function:

testfor:-
List = [['Test1', 'US'], ['Test1', 'France'], ['Test2', 'German']],
(
  foreach(X,List) do
    X=[F,S],
    append([F],[],List1),
    append([S],[],List2)
),
writeln(List1).

It doesn't work, I am confused. Where did I make a mistake? Of course, you can help me implement this function using your method (i.e. "don't use foreach").

Upvotes: 1

Views: 5296

Answers (4)

Palec
Palec

Reputation: 13574

Using maplist/4, the whole iteration is hidden and you have to think just about the deconstruction of a single pair.

%zip(?FirstList, ?SecondList, ?PairList)
zip(Fst, Snd, Pair) :- maplist(pair, Fst, Snd, Pair).

%pair(?First, ?Second, ?Pair)
pair(Fst, Snd, [Fst, Snd]).

Use it thus:

?- zip(List1, List2, [['Test1', 'US'], ['Test1', 'France'], ['Test2', 'German']]).
List1 = ['Test1', 'Test1', 'Test2'],
List2 = ['US', 'France', 'German'].

Note that this is an advanced solution, though. First, getting to know the basic recursive solutions (presented by other answers) is really useful and I recommend it. Without a good grasp of recursion, Prolog is just one big hell. Actually, maplist uses recursion too, it is just hidden from the caller.

Another thing to learn is how Prolog terms and unification work.

  • Using append is completely unnecessary here. Moreover, it is harmful both performance-wise and readability-wise.
  • Pairs are better represented using a functor, e.g. pair ['Test1', 'US'] becomes pair('Test1', 'US'). Among other benefits, it is harder to accidentally mix up pairs and triplets. Using functors is somewhat similar to using types in other languages.

    SWI-Prolog uses hyphen-minus as the functor for pairs. Due to having an operator declaration, hyphen-minus can be used in infix notation, too. E.g. pair ['Test1', 'US'] becomes 'Test1'-'US'. Infix notation is just syntactic sugar for the usual prefix notation used for other functors (i.e. 'Test1'-'US' == -('Test1', 'US')).

Finally, the foreach ... do notation you tried to use looks like a loop construct from ECLiPSe, which is a language derived from Prolog. SWI-Prolog does not support this syntax.

Upvotes: 2

André Abreu
André Abreu

Reputation: 757

testfor([], [], []).
testfor([[X, Y]|T1], [X|T2], [Y|T3]):-testfor(T1, T2, T3).

Upvotes: 0

Kaarel
Kaarel

Reputation: 10672

pairs_to_lists([], [], []).

pairs_to_lists([E1-E2 | Tail], [E1 | Tail1], [E2 | Tail2]) :-
    pairs_to_lists(Tail, Tail1, Tail2).

Usage:

?- pairs_to_lists(['Test1'-'US', 'Test1'-'France', 'Test2'-'German'], L1, L2).
L1 = ['Test1', 'Test1', 'Test2'],
L2 = ['US', 'France', 'German'].

Some comments:

  1. A list is an overkill if you know the number of elements in it, e.g. instead of [A, B] use A-B or pair(A, B).
  2. append([A], L1, L2) is the same as L2 = [A | L1], the latter is more readable.

Upvotes: 1

Well

yoba( [], [], [] ).
yoba( [[Name1, Name2] | Tail], List1, List2 ) :-
    append( [Name1], ListNew1, List1 ),
    append( [Name2], ListNew2, List2 ),
    yoba( Tail, ListNew1, ListNew2 ).

And

?- yoba([['Test1', 'US'], ['Test1', 'France'], ['Test2', 'German']], X, Y).
X = ['Test1', 'Test1', 'Test2'],
Y = ['US', 'France', 'German'].

Upvotes: 1

Related Questions