2

I need an ordered list of Objects that satisfy Goal. setof takes care of the ordering, but fails when no Objects satisfy Goal. I want to return an empty list instead like findall does.

This works, but is there a way of accomplishing this without a cut? I'm using SWI-Prolog.

setof(Object, Goal, List), !; List = [].
false
  • 10,264
  • 13
  • 101
  • 209
brabec
  • 4,632
  • 8
  • 37
  • 63

3 Answers3

5

First,

..., ( setof(Object, Goal, List), ! ; List = [] ), ...

does not work, as you suggest. It always succeeds for List = [], and it only shows the first answer of setof/3. But setof/3 may produce several answers. The general method that works in any Prolog is:

..., ( \+ Goal -> List = [] ; setof(Object, Goal, List) ), ...

Many implementations offer an implementation specific control construct for this which avoids that Goal is called twice. E.g. if/3 (SICStus, YAP), or (*->)/2 (SWI, GNU):

..., if( setof(Object, Goal, ListX), ListX = List, List = [] ), ...

..., ( setof(Object, Goal, ListX) *-> ListX = List ; List = [] ), ...

The new variable ListX is necessary for the (admittedly rare) case that List is already instantiated.

Note that both other answers do not produce exactly what you asked for.

false
  • 10,264
  • 13
  • 101
  • 209
  • 1
    Thanks for the detailed explanation. I guess jschimpf answer misses to preserve the generality you see in OP question. Hope I'm right on this, at least :) – CapelliC Dec 13 '13 at 07:49
1
(setof(Object, Goal, List) ; List = [])

will work as well (setof itself is deterministic).

To be sure to get rid of the choice point, we need a bit more verbose

(setof(Object, Goal, List) -> true ; List = [])

edit as it stands, my answer is plainly wrong, or at least very incomplete. After false comments, and answer, I would suggest

setof(Object, Goal, List) *-> true ; List = [].
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • -1: Both answers are incorrect: The first will **always** produce a list, the second will remove wanted alternatives. – false Dec 12 '13 at 21:16
0

If you do not need the potential nondeterminism or the variable-quantification features of setof, you can stick with findall/3. This is deterministic and doesn't fail:

?- findall(X, fail, Xs).
Xs = []
yes

You can then sort the results yourself using sort/2:

findall(Object, Goal, UnsortedWithDuplicates),
sort(UnsortedWithDuplicates, List)
jschimpf
  • 4,904
  • 11
  • 24