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 = [].
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.
(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 = [].
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)
来源:https://stackoverflow.com/questions/20407430/prolog-how-to-do-setof-that-returns-empty-list-instead-of-failing