Prolog Undefined Procedure - prolog

I'm trying to to get a list of three items with their relevant information based on a collection of information:
product(I):-
I = [_,_,_,_], %Type,Brand,Category,Value
cheaper(item(apple,_,_),item(_,kay,_,_),I),
cheaper(item(bar,_,_,_),item(_,_,fruit,_),I),
member(item(_,kay,_,2),I),
member(item(apple,granny,_,_),I),
member(item(bar,_,chocolate,_),I),
/* Below not given */
member(item(cracker,_,_,_),I),
member(item(_,_,biscuit,_),I),
member(item(_,_,_,4),I),
member(item(_,_,_,5),I).
cheaper(X,Y,H) :- %Used to determine the item values
item(X,_,_,A),
item(Y,_,_,B),
A<B.
When I try running it I encounter an error:
?- product(I).
ERROR: cheaper/3: Undefined procedure: item/4
Exception: (8) item(item(apple, _G3604, _G3605), _G3651, _G3652, _G3653) ?
I understand that item isn't a procedure, however what can I used for checking the value for apple against the value for bar?

First, the obvious note, you're calling cheaper wrong once:
cheaper(item(apple,_,_),item(_,kay,_,_),I),
↑
Only three values, not four.
If item isn't a procedure, you mustn't call it, but use destructuring.
Also you want those items you're checking with cheaper to be part of the list, right? If so, you'll have to check that. And you can use unification to extract the values that you need:
cheaper(X,Y,I) :-
member(X,I),
member(Y,I),
[item(_,_,_,A),item(_,_,_,B)] = [X,Y],
A<B.
Now you'll get some errors regarding not instantiated argument. That's because you are checking not (yet) set variables if they are greater than each other. To avoid this, move the cheaper/3 calls to the end of your clause body:
product(I):-
I = [_,_,_,_], %Type,Brand,Category,Value
member(item(_,kay,_,2),I),
member(item(_,_,_,4),I),
member(item(_,_,_,5),I),
member(item(apple,granny,_,_),I),
member(item(bar,_,chocolate,_),I),
/* Below not given */
member(item(cracker,_,_,_),I),
member(item(_,_,biscuit,_),I),
cheaper(item(apple,_,_,_),item(_,kay,_,_),I), % note the 4th argument
cheaper(item(bar,_,_,_),item(_,_,fruit,_),I).
With this, you'll get one solution and then it fails with an error. This is, because you only give three values for the price slot and you have four items and prolog will check A > 2.
Sorry, in my other answer I didn't look for what the poster was trying to achieve and I think that this is better than a complete reedit. (glorious SO mods let me know if I'm wrong)

Related

SWI-Prolog Replacing an Atom value by another value

I am a newbie to SWI-Prolog and am facing an issue. So I have these Atoms named GUID1 and GUID2. Initially suppose GUID1 = 'ABC'. After following statement:
gensym(GUID1,GUID2)
GUID2 becomes 'ABC1'. Now I need to concatenate this ABC1 with prefix say 'X', the result by concatenate is received in new Atom say NGUID2. I used
atom_concat(P, GUID2, NGUID2),
P has 'X' as the value.
For now, NGUID2 is successfully bound to 'XABC1'. But For some reason I want this 'XABC1' value to bind to GUID2 as otherwise, it will complicate my other work. Basically I want to reassign GUID2 to value of NGUID2.
Edit 1: I tried b_setval(GUID2, NGUID2) but, it is not updating GUID2. So I'm still stuck.
Edit 2: So as per one of the comments I will specify why I want to do this.
Predicate definition
This is the original implementation which I need to modify. And When I call the predicate with following call.
Calling
The Clone_ID in calling predicate gets bound to the GUID value +1,+2,+3... Eg if GUID value is ABC then Clone_ID will become ABC1 for first call, ABC2 for second call and so on. But Now I want to append a prefix X to Each new value of Clone_ID, so it should be something like XABC1, XABC2, ... and so on. How should I modify the function definition in-order to do so.

Sort a parameter value in ascending orders in Prolog

I have a list of facts that have those parameters: Name, Longitude, Latitude. I want to write a predicate that sorts the Latitude only.
Here's part of my facts.
pool(roy, -75.702744, 45.4089761).
pool(marth, -75.731638, 45.3803301).
pool(jiggy, -75.7449645, 45.40431589).
pool(yamaha, -75.7114829, 45.3993461).
I tried to do something of the following but didn't get lucky:
furthest(Lat-Long):- setof(Lat-Long, pool(_, Long, Lat), [Lat-Long|_]).
Any ideas of how I should tackle this?
does this work?
pool(roy, -75.702744, 45.4089761).
pool(marth, -75.731638, 45.3803301).
pool(jiggy, -75.7449645, 45.40431589).
pool(yamaha, -75.7114829, 45.3993461).
my_sort:-
findall(forsort(Lat,Name),pool(Name,Long,Lat),List),
msort(List,Sorted),
write(Sorted).
?- my_sort.
[forsort(45.3803301,marth),forsort(45.3993461,yamaha),forsort(45.40431589,jiggy),forsort(45.4089761,roy)]
true.
Excerpt of manual below(SWI):
msort sorts List to the standard order of terms
Standard Order of Terms:
Compound terms are first checked on their arity, then on their functor name (alphabetically) and
finally recursively on their arguments, leftmost argument first.

sort values of an orddict

In order to extract the values (records) of an orddict as a sorted list, tried this:
-module(test).
-compile(export_all).
-record(node, {name="", cost=0}).
test() ->
List = orddict:append("A",#node{name="A",cost=1},
orddict:append("B",#node{name="B",cost=2},
orddict:new())),
lists:sort(fun({_,A},{_,B}) -> A#node.cost =< B#node.cost end,
orddict:to_list(List)).
The sort fails with exception error: {badrecord,node}.
What would be the correct syntax?
Solved:
The correct insertion method is orddict:store/2 instead of orddict:append/2. Then the pattern {_,A} matches for the comparison function.
The correct syntax is:
lists:sort(fun({_,[A]},{_,[B]}) -> A#node.cost =< B#node.cost end,
orddict:to_list(List)).
I not found note about this in documentation,but you can look in source code of module.
As #Pascal write in comments the reason is that orddict:append/3 is a function provided to append a value to an existing Key/Value pair where Value must be a list. In the use case, the key doesn't exist, so the pair is created and the Value append to an empty list.
Btw, you always can print and compare real and expected result.
io:format("~p~n",[orddict:to_list(List)])
For your example that is:
[{"A",[{node,"A",1}]},{"B",[{node,"B",2}]}]

Why don't I need to check in the member_of_set if Element and Element1 are different?

When using sets, adding an Element to the Set is done like this:
add_to_set(Element, [], [Element]).
add_to_set(Element, [Element | Set], [Element | Set]).
add_to_set(Element, [Element1 | Set], [Element1 | NewSet]) :-
not(Element = Element1),
add_to_set(Element, Set, NewSet).
Now, with this, I thought member_of_set would be like:
member_of_set(Element, [Element|_]).
member_of_set(Element, [Element1|Set]) :-
not(Element = Element1), /* Not necessary */
member_of_set(Element, Set).
This works like a charm, but in this case, not(Element = Element1) is not necessary. I can't seem to figure out why. If you ask for more answers from Prolog, won't it backtrack and succeed on the second clause of member_of_set?
An if it's not necessary in the member_of_set, then why is it necessary in the add_to_set?
Please keep in mind that I'm only studying since one month Prolog, so I'm still in some kind of mind switch...
I know that using cut, there's probably better alternatives, but cut shouldn't be used.
it's not necessary in the member_of_set
because it doesn't hurt if the list is not a set. Only you will end up - in case you pass a list with repeated elements inside - with multiple solution, but still each solution is valid.
OTOH, add_to_set leads to invalid data if you remove the test:
?- add_to_set(1,[],A),add_to_set(1,A,B).
A = B, B = [1] ;
A = [1],
B = [1, 1] ;
false.
for a friendly explanation of cuts, and why are necessary in Prolog, see this page
contains(Element, [Element|_]).
contains(Element, [_|Set]) :-
contains(Element, Set).
You can read this as: "Element is in a Set, if it's the head of that Set, or if it's in the tail of that Set". If you're checking if it's in the tail, there's no need to check if it's equal to the head, except for performance reasons.
Actually, this way, you're not even restricting it to a Set. It's a general contains.

translate(list1, list2) in prolog

I was trying a functor translate([3,5,1,3],[three,five,one,three]) which does the operation of printing numbers. I get a strange warning while executing like this,
35 ?- translate([1,2,3],[a,b,c]).
ERROR: write/2: stream `a' does not exist
domains
list1=integer*
list2=symbol*
predicates
translate(list1,list2)
means(integer,symbol)
clauses
translate([],[]).
translate([],_):-
write("\nError in Input").
translate(_,[]):-
write("\nError in Input").
translate([Head1|Tail1],[Head2|Tail2]):-
write(Head2," = "),
means(Head1,Name),
write(Name,"\n"),
translate(Tail1,Tail2).
means(0,zero).
means(1,one).
means(2,two).
means(3,three).
means(4,four).
means(5,five).
means(6,six).
means(7,seven).
means(8,eight).
means(9,nine).
What exactly is the problem? This is the expected value.
translate([1,2,3],[a,b,c])
a = one
b = two
c = three
Yes
Variables need to be uppercase:
translate([1,2,3],[A,B,C]).
When you enter the translate([Head1|Tail1],[Head2|Tail2]) clause, a unifies with Head2, and then you try to satisfy write(Head2, "="), which is write(a, "=").
write/2 takes as first argument a Stream and writes the second argument to that Stream.
Presumably you want to use - if you want output at all - something like
writef('Head2 = %w', [Head2])
(I got the formatting from here.)

Resources