2

I have the following problem:

prolog prog:

man(thomas, 2010).
man(leon, 2011).
man(thomas, 2012).
man(Man) :- once(man(Man, _).

problem:

?- man(thomas).
true ;        %i want only on true even if there are more "thomas" *working because of once()*

?- man(X).
X = thomas ;  %i want all man to be listed *isn't working*

goal:

?- man(thomas).
true ;

?- man(X).
X = thomas ;
X = leon ;
X = thomas ;

I do unterstand why this happens, but still want to get the names of all man. So my solution woud be to look if "Man" is initialized, if yes than "once.." else then... something like that:

man(Man) :- (->check<-,once(man(Man, _)); man(Man, _).

On "check" shoud be the code sniped that checks if the variable "Man" is filled.

Is this possible?

false
  • 10,264
  • 13
  • 101
  • 209
Spenhouet
  • 6,556
  • 12
  • 51
  • 76
  • 1
    With your existing code, I don't get an error when I enter `man(X)`. I get `X = thomas`. And if I enter `man(_)` I get `true`. If you want to check if a variable has already been instantiated, you can use `var(Man)` (true means uninstantiated). – lurker Jan 05 '14 at 22:36
  • Year, right that was wrong from me but at X and _ i wan't all man to be listed. That's not the case. Sounds like var(Man) is what i need. But m(Mann) :- ((not(var(Mann))), once(m(Mann, _))); m(Mann, _). allways choses m(Mann, _). – Spenhouet Jan 05 '14 at 22:46
  • You can't get results in an anonymous variable like `_`. It will only respond true of false since it's anonymous! You need `X` or something like that. Regarding your predicae, please update (edit) your problem statement with your additional information for clarity. :) – lurker Jan 05 '14 at 22:49
  • I tried your code update and it does exactly as programmed. If `Mann` is uninstantiated, it chooses `m(Mann, _)` because that's what the logic says to do. And if `Mann` is instantiated (e.g., you enter `man(thomas).` then the `once(m(Mann,_))` is chosen. I'm not sure I understand what logic you are wanting to establish. – lurker Jan 05 '14 at 23:00
  • I'm confused. In your last comment you are telling exectly what i want to establish. :) but this code i posted in the comment doesn't work for me. `man(thomas).` gives me a true 3 times. :/ – Spenhouet Jan 05 '14 at 23:07
  • In my post i added this solution and the outcome 1:1. Where is the difference to your code? – Spenhouet Jan 05 '14 at 23:16
  • I wasn't saying that `man(thomas)` gave only one `true` but only that it gave `true`. It has multiple solutions because (a) there are two database items that have `thomas` so it finds both, and (b) it will succeed once more due to your "or" clause `;`. – lurker Jan 05 '14 at 23:26
  • That is the problem why i'm doing this "once" thing. I need only one true when thomas is a man `man(thomas).` but man(X) and man(_) shoud function normaly. – Spenhouet Jan 05 '14 at 23:30
  • Year nice. I also tried this a second ago and it works :) Pls. post this as solution so i can give you credit for your help and time :) – Spenhouet Jan 05 '14 at 23:35
  • Cool. Sorry I was being so daft - took me awhile to understand what you wanted to achieve. – lurker Jan 05 '14 at 23:36
  • Sorry that i coudn't make that clear. But thank you very much. You helped me allot. ps. i just found that there is a nonvar() instead of not(var()). Just to improve. – Spenhouet Jan 05 '14 at 23:41
  • Yes I forgot about `nonvar`. Good catch. :) – lurker Jan 05 '14 at 23:41
  • Your right. the once() also isn't needed anymore. :) Now i'm really happy with this nice solution and can go to sleep^^ It's 00:44 here. – Spenhouet Jan 05 '14 at 23:44

2 Answers2

4

One way to achieve this is as follows:

man(X) :-
    (nonvar(X), man(X, _)), !
    ;
    man(X, _).

Or, more preferred, would be:

man(X) :-
    (   var(X)
    ->  man(X, _)
    ;   once(man(X, _))
    ).

The cut will ensure only one solution (at most) to an instantiated X, whereas the non-instantiated case will run its course. Note that, with the cut, you don't need once/1. The reason once/1 doesn't work as expected without the cut is that backtracking will still come back and take the "or" condition and succeed there as well.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • Thanks @false - in this case, the `once/1` (at least the way the OP attempted it) allowed backtracking to find a single name like `thomas` multiple times when queried as `man(thomas)`, which the OP was working to avoid. I may not have expressed it properly, however, to make it work as desired. I think the `setof` solution you have works best, assuming the OP doesn't want the redundant solutions as implied by their question. – lurker Jan 06 '14 at 01:10
  • @false oh, great catch. I didn't even think about it after I modified the flow, that is much better (edited). – lurker Jan 06 '14 at 01:32
  • @lurker I have an exam scheduler in prolog. If it is an instantiated X, I first need to sort my X(list). After that I can check if the list is valid (or uninstantiated - generate a schedule). But I checked with a "count_proofs" function, and when I add this check it gives me 317952 results. Without the check, (just generating) it gives me 9936 results. Why does this check give me more results? – Ken Vernaillen Dec 25 '15 at 20:51
  • @KenVernaillen why don't you post a stackoverflow question showing the relevant code? Without seeing the code, it's difficult to answer your question. – lurker Dec 25 '15 at 21:49
4
man(X) :-
   setof(t,Y^man(X,Y),_).

Additionally to what you are asking this removes redundant answers/solutions.

The built-in setof/3 describes in its last argument the sorted list of solutions found in the first argument. And that for each different instantiation of the free variables of the goal. Free variables are those which neither occur in the first argument nor as an existential variable – the term on the left of (^)/2.

In our case this means that the last argument will always be [t] which is uninteresting. Therefore the _.

Two variables occurring in the goal are X and Y. Or, to be more precise the variables contained in X and Y. Y is an existential variable.

The only free variable is X. So all solutions for X are enumerated without redundancies. Note that you cannot depend on the precise order which happens to be sorted in this concrete case in many implementations.

false
  • 10,264
  • 13
  • 101
  • 209
  • It works fine (also sorted the names). But i must admit that i don't understand how it's working. Why that `t` and what is `Y^` doing? I know setof is looking for something (`t`) in a list (`_`) witch is created from (`Y^man(X,Y)`) or? – Spenhouet Jan 06 '14 at 13:29