Search code examples
prologprolog-setof

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


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 = [].

Solution

  • 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.