问题
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 = [].
回答1:
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.
回答2:
(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 = [].
回答3:
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