4

I've written the following predicate:

list_withoutlast([_Last], []). % forget the last element
list_withoutlast([First, Second|List], [First|WithoutLast]) :-
  list_withoutlast([Second|List], WithoutLast).

Queries like list_withoutlast(X, [1, 2]). succeed deterministically, but queries like list_withoutlast([1, 2, 3], X) leave behind a choicepoint, even though there's only one answer.

When I trace it seems to be that SWI attempts to match list_withoutlast([3], Var) against both clauses, even though definitely only the first one will ever match!

Is there something else I can do to tell SWI that I want a list with more than one element? Or, if I want to take advantage of first-argument indexing, are my only options "zero-length list" and "non-zero length list"?

Do other Prologs handle this situation any differently?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
num1
  • 4,825
  • 4
  • 31
  • 49
  • @Paulo Why did you remove the SWI-Prolog tag? – num1 Jul 11 '18 at 19:02
  • 2
    The question is generic. Thus, by using the generic `Prolog` tag, we're telling others that the same issue and solutions apply to other Prolog implementations. – Paulo Moura Jul 11 '18 at 19:12

1 Answers1

4

You can rewrite your predicate to avoid the spurious choice point:

list_withoutlast([Head| Tail], List) :-
    list_withoutlast(Tail, Head, List).

list_withoutlast([], _, []).
list_withoutlast([Head| Tail], Previous, [Previous| List]) :-
    list_withoutlast(Tail, Head, List).

This definition takes advantage of first-argument indexing, which will distinguish in the list_withoutlast /3 predicate the first clause, which have an atom (the empty list) in the first argument, from the second clause, which have a (non-empty) list in the first argument.

Passing the head and tail of an input list argument as separate arguments to an auxiliary predicate is a common Prolog programming idiom to take advantage of first-argument indexing and avoid spurious choice-points.

Note that most Prolog systems don't apply deep term indexing. In particular, for compound terms, indexing usually only takes into account the name and arity and doesn't take into account the compound term arguments (a list with one element and a list with two or more elements share the same functor).

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33