Prolog - how to do setof that returns empty list instead of failing

不羁的心 提交于 2019-11-29 12:12:17

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!