Does SWI-Prolog avoid redundant searches, and if so how?

Consider the two following PL queries:

L = [1,2,3], member( X, L ).
L = [1,2,3], member( X, L ), member( X, L ).

Would both of them have essentially the same complexity and execution time? Or is the first one O(n) whereas the second is O(n^2)? Does SWI (or any other PL implementation) have logic that avoids a redundant second iteration through the members of L? If so, how is this done?

clp(fd) might do what you are wondering about but it doesn’t do so the way you might think.

If so, how is this done?

It simplifies the logic based on the constraints and if possible will return all variables grounded, or simplify the expression as much as possible.


Since your question is a bit open-ended, tabling might also be considered a possible answer.

You can trace this to see what happens. :wink:

In the second query, the first member(X,L) will instantiate X, so the second member(X,L) will simply look for X in L rather than backtracking through all possibilities. But it’ll still be O(n^2) because the search has to go through all items (memberchk(X,L) would stop as soon as X is found, so it’d be a bit faster and also wouldn’t leave a choicpoint).

Now, try adding a table/1 declaration and you’ll see a different trace – the table/1 will result in a kind of memoization. In this particular situation, it’s not clear that there’ll be any performance advantage because the memoization will still need to match against the entire L list and that’ll be O(n). But for more complex queries, there can be a big performance improvement.

1 Like