Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

executing operation for each list element in swi-prolog and others

Tags:

prolog

How do I do an operation for each element of a list, in order?

Based on these two resources:

  1. http://www.swi-prolog.org/pldoc/doc/swi/library/lists.pl
  2. http://www.swi-prolog.org/pldoc/doc_for?object=foreach/2

I imagine I can always rely on:

  • foreach(member(X, [1,2]), write(X)).

Is that deterministic and can I wrap the member/2 predicate as I please in my own predicates and still always iterate in order?

like image 259
codeshot Avatar asked Sep 24 '11 08:09

codeshot


People also ask

How do I iterate a list of lists in Prolog?

Generally, you do not iterate in Prolog. Instead, you write a rule with a pair of recursive clauses, like this: dosomething([]). dosomething([H|T]) :- process(H), dosomething(T).

How do you access list elements in Prolog?

Unlike arrays in other programming languages where we can directly access any element of the array, prolog lists allow direct access of the first element only which is denoted as Head. Therefore we can write a prolog list as : [Head | Rest], where Rest is the rest of the list excluding the first element Head.

How do you get the second element in a list in Prolog?

You can move unification into the head of the clause and simply write: second([_, Second| _], Second). The notation for lists is to write the initial elements separated by commas and then a vertical bar to separate the list tail, i.e. the list containing the rest of the elements.

What is list in Prolog explain with example?

It is a data structure that can be used in different cases for non-numeric programming. Lists are used to store the atoms as a collection. In the subsequent sections, we will discuss the following topics − Representation of lists in Prolog. Basic operations on prolog such as Insert, delete, update, append.


1 Answers

Yes, but you have to worry about your predicate failing. If it can, the remaining elements in the list will not be processed because it produces a conjunction rather than a failure-driven loop.

I would be more keen to use maplist/2 since I think it is more widely used than foreach/2 but I also hadn't seen this option before. :)

Edit: Let's discuss what I mean about failure-driven loops.

There are two primitive iteration methods in Prolog: recursion and failure-driven loops. Say I want to print out every item in a list. The recursive method is going to look like this:

print_all([]).
print_all([X|Rest]) :- write(X), nl, print_all(Rest).

So given a list like [1,2,3], this is going to expand like so:

print_all([1,2,3])
  write(1), nl, print_all([2,3])
    write(1), nl, write(2), nl, print_all([3])
      write(1), nl, write(2), nl, write(3), nl, print_all([])
        write(1), nl, write(2), nl, write(3), nl.

This is how member/2 is usually implemented:

member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).

So you can see the recursive method is pretty simple and general.

Another simple but somewhat frowned-upon method is to simulate a failure to engage the backtracking mechanism. This is called a failure-driven loop and looks like this:

print_all(List) :- member(X, List), write(X), nl, fail.
print_all(_).

When you run this version of print_all/1, what happens is a little more complex than simple expansion.

print_all([1,2,3])
  member([1,2,3], 1)
    write(1), nl
      fail
  retry member([1,2,3], 2)
    write(2), nl
      fail
  retry member([1,2,3], 3)
    write(3), nl
      fail
retry print_all(_)
  true

Verbally, the fail forces Prolog to back up to the last choice point it made and try using the next solution. Well, write/1 and nl/0 don't produce choice points because they only have one solution, but member/2 does have multiple solutions—one for each item in the list. So Prolog takes each item out of the list and prints it. Finally when member/2 runs out of solutions, Prolog backs up to the previous choice point, which is the second body of the print_all/1 predicate, which always succeeds. So the output looks the same. I think people nowadays generally prefer not to use failure-driven loops, but I don't understand the arguments well enough to parrot them usefully.

One thing that may help you to see what's going on is use the trace predicate and step through the expansion of both versions, and see if you can make sense of the differences. My notation above is completely made up for this answer and may not be as clear.

Looking back over what I originally wrote and your actual question:

  • foreach is going to be deterministic
  • member will always iterate in order, because lists are defined in such a way that you must access each item in turn

Moreover, these days at least on S.O. you will get a lot of people telling you to use maplist and its ilk, so it's probably not just going to work, but also a good idea.

like image 157
Daniel Lyons Avatar answered Oct 16 '22 05:10

Daniel Lyons