Assign variable in list - list

I've got a problem with Prolog lists.
Let's say I've got this predicate:
array(p, [A,B,C]).
When I do:
array(p,X).
I got: X = [_,_,_]
Now, considering I've got this predicate:
p1(1) :- array(p1, [1,B1,C1]).
I expected to get:
X = [1,_,_]
but instead, the result is the same as before. Is such a thing even possible in Prolog? Another question is if somehow we can set these values, could we overwrite these values in the same way? I understand that in the prolog variables are assigned only once but I would like to somehow get a dynamic list.

I'm not sure what you mean by "paradigm," and I'm very unclear on what you're trying to do with this code. If you have this at the toplevel:
array(p, [A,B,C]).
you are defining a fact array/2, which associates p with a list of three uninstantiated variables. Your first query amounts to retrieving this fact.
Your second "paradigm" is really the definition of a rule or predicate p1/1, which takes a single argument, which must be 1 for the rule to fire. The body of this second predicate is a call to the predicate array/2 which is definitely going to fail. I don't see how you could possibly get the same result as before, because you defined array(p, ...) before and now you are looking for array(p1, ...). Furthermore, there is no X in your second query, so there is no reason for X to appear in the result, and it definitely would not, even if you had called array(p, ...) instead of array(p1, ...).
I think what you're trying to do here is probably set up some kind of set of three variables and then unify each of them in turn as you proceed along some calculation. To do something like that is possible and easy in Prolog, but the fact database is not going to participate in this process really. You're going to have to write predicates that pass your variables along to other predicates that will unify them and return them bound. None of this is very hard, but it looks like you're going to have to go back and understand the fundamentals here a little better. You're far enough off track here that I don't think anyone can really answer your question as stated, because there's too much confusion in it.

Related

How to detect list changes without comparing the complete list

I have a function which will fail if there has being any change on the term/list it is using since the generation of this term/list. I would like to avoid to check that each parameter still the same. So I had thought about each time I generate the term/list to perform a CRC or something similar. Before making use of it I would generate again the CRC so I can be 99,9999% sure the term/list still the same.
Going to a specfic answer, I am programming in Erlang, I am thinking on using a function of the following type:
-spec(list_crc32(List :: [term()]) -> CRC32 :: integer()).
I use term, because it is a list of terms, (erlang has already a default fast CRC libraries but for binary values). I have consider to use "erlang:crc32(term_to_binary(Term))", but not sure if there could be a better approach.
What do you think?
Regards, Borja.
Without more context it is a little bit difficult to understand why you would have this problem, particularly since Erlang terms are immutable -- once assigned no other operation can change the value of a variable, not even in the same function.
So if your question is "How do I quickly assert that true = A == A?" then consider this code:
A = generate_list()
% other things in this function happen
A = A.
The above snippet will always assert that A is still A, because it is not possible to change A like you might do in, say, Python.
If your question is "How do I assert that the value of a new list generated exactly the same value as a different known list?" then using either matching or an actual assertion is the fastest way:
start() ->
A = generate_list(),
assert_loop(A).
assert_loop(A) ->
ok = do_stuff(),
A = generate_list(),
assert_loop(A).
The assert_loop/1 function above is forcing an assertion that the output of generate_list/0 is still exactly A. There is no telling what other things in the system might be happening which may have affected the result of that function, but the line A = generate_list() will crash if the list returned is not exactly the same value as A.
In fact, there is no way to change the A in this example, no matter how many times we execute assert_loop/1 above.
Now consider a different style:
compare_loop(A) ->
ok = do_stuff(),
case A =:= generate_list() of
true -> compare_loop(A);
false -> terminate_gracefully()
end.
Here we have given ourselves the option to do something other than crash, but the effect is ultimately the same, as the =:= is not merely a test of equality, it is a match test meaning that the two do not evaluate to the same values, but that they actually match.
Consider:
1> 1 == 1.0.
true
2> 1 =:= 1.0.
false
The fastest way to compare two terms will depend partly on the sizes of the lists involved but especially on whether or not you expect the assertion to pass or fail more often.
If the check is expected to fail more often then the fastest check is to use an assertion with =, an equivalence test with == or a match test with =:= instead of using erlang:phash2/1. Why? Because these tests can return false as soon as a non-matching element is encountered -- and if this non-match occurs near the beginning of the list then a full traverse of both lists is avoided entirely.
If the check is expected to pass more often then something like erlang:phash2/1 will be faster, but only if the lists are long, because only one list will be fully traversed each iteration (the hash of the original list is already stored). It is possible, though, on a short list that a simple comparison will still be faster than computing a hash, storing it, computing another hash, and then comparing the hashes (obviously). So, as always, benchmark.
A phash2 version could look like:
start() ->
A = generate_list(),
Hash = erlang:phash2(A),
assert_loop(Hash).
assert_loop(Hash) ->
ok = do_stuff(),
Hash = erlang:phash2(generate_list()),
loop(Hash).
Again, this is an assertive loop that will crash instead of exit cleanly, so it would need to be adapted to your needs.
The basic mystery still remains, though: in a language with immutable variables why is it that you don't know whether something will have changed? This is almost certainly a symptom of an underlying architectural problem elsewhere in the program -- either that or simply a misunderstanding of immutability in Erlang.

Why is the built in delete predicate not working on this dynamic list? (Prolog)

I have a dynamic list:
:- dynamic queue/1.
queue([Bob,Steve,Richard,Katie]).
and a predicate:
delete_person(Person) :-
queue(Q),
retractall(queue(_)),
delete(Q, Person, Z),
assert(queue(Z)).
Logically, this follows to me, but I've obviously made a mistake as the list stays the same after running the predicate delete_person
There are multiple problems in this snippet. First of all, you have posted the following program:
:- dynamic queue/1.
queue([Bob,Steve,Richard,Katie]).
delete_person(Person) :-
queue(Q),
delete(Q, Person, Z).
assert(queue(Z)).
This means that you have a fact of the form assert/1 in your code. That's probably not what you intended to write.
Second, if you correct delete_person/1 to read:
delete_person(Person) :-
queue(Q),
delete(Q, Person, Z),
assert(queue(Z)).
then we still have the following singleton warning:
Singleton variables: [Bob,Steve,Richard,Katie]
This is because Bob, Steve etc. are all (probably inadvertently) Prolog variables.
If you correct this to read:
queue([bob,steve,richard,katie]).
Then at least the program compiles without warnings.
Now we can talk about the actual issue:
?- delete_person(bob).
true.
?- queue(Q).
Q = [bob, steve, richard, katie] ;
Q = [steve, richard, katie].
Next time, please include:
the query you tried
the expected result
the obtained result.
In the case above, why is the queue not modified?
That's because you inadvertently only add an additional fact for queue/1, but you do not remove the already existing fact.
That's a typical mistake when modifying the global database. You are already getting a glimpse of the issues that await you if you use Prolog like an imperative language. It will make your code extremely hard to reason about, yield several hard to debug problems, makes it hard to test your code, and generally combines the disadvantages of imperative languages with their severe lack of generality.
A declarative solution of your program is to think in terms of relations between queues. Think of it this way: What is the relation between 2 queues if a specific element is removed from the first queue, and the second queue is the same as the first queue except for that element?
For example, think about: queue_without_element/3, whose definition could start like this:
queue_without_element(Q0, E, Q) :-
...
Advantages:
Usable in several directions.
Can be tested in isolation of other predicates.
Is automatically thread safe.
Is almost certainly more efficient than modifying the global database.
etc.

Prolog Predicate Solution

I am going through some past exam questions for my prolog exam that is coming up.
Here is the question:
(a) Write a predicate insert(Xs, Y, Zs) that holds when Zs is the list obtained
by inserting Y into the list Xs. A query such as:
? - insert([1,2,3], 4, Zs).
should succeed four times and give the following answers:
Zs = [4, 1, 2, 3]
Zs = [1, 4, 2, 3]
Zs = [1, 2, 4, 3]
Zs = [1, 2, 3, 4].
I'm a bit concerned because I have no idea where to start. Would anyone be able to help out as I need example solutions to practice for my exam.
Would really appreciate any help with this.
We start by changing the terrible name of this predicate: The predicate should describe what holds, not what to do. The name should reflect that. I suggest list_with_element/3, and encourage you to try finding even better names, ideally making clear what each argument stands for.
Then, we do what we set out to do: Describe the cases that make this relation hold.
For example:
list_with_element([], E, [E]).
list_with_element([L|Ls], E, [E,L|Ls]).
list_with_element([L|Ls0], E, [L|Ls]) :-
...
I leave filling in the ... as an easy exercise. State the condition that is necessary for the clause head to be true!
EDIT: I would like to say a bit more about the pattern above. In my experience, a good way—and definitely in the beginning—to reason about predicates that describe lists is to consider two basic cases:
the atom [], denoting the empty list
terms of the form '.'(E, Es), also written as [E|Es], where E is the first element of the list and Es is again a list.
This follows the inductive definition of lists.
The drawback in this specific case is that this approach leads to a situation where case (2) again needs to be divided into two subcases, and somehow unexpectedly necessitates three clauses to handle the two basic cases. This obviously runs counter to our intuitive expectation that two clauses should suffice. Indeed they do, but we need to be careful not to accidentally lose solutions. In this case, the first two clauses above are both subsumed by the fact:
list_with_element(Ls, E, [E|Ls]).
Every experienced Prolog coder will write such predicates in this way, or just, as in this case, use select/3 directly. This is what #lurker sensed and hinted at, and #tas correctly shows that a different clause (which is easy to come up with accidentally) does not fully subsume all cases we want to express.
Thus, I still find it a lot easier to think first about the empty list explicitly, make sure to get that case correct, then continue with more complex cases, and then see if you can write the existing program more compactly. This is the way I also used for this sample code, but I did not make it as short as possible. Note that with monotonic code, it is completely OK to have redundant facts!
Note that is is specifically not OK to just replace the first two clauses by:
list_with_element([L|Ls], E, [E|Ls]).
because this clause does not subsume case (1) above.
I guess that one answer that the question might be looking for goes along these lines:
insert(List, Element, NewList) :-
append(Front, Back, List), % split list in two
append(Front, [Element|Back], NewList). % reassemble list
If you would like a declarative reading:
NewList has Element between the front and the back of List.
Check carefully if append/3 or a predicate with the same semantics appears in the earlier questions or the study material.
And note that this is in essence the exact same solution as the suggestion by #mat, if I understand it correctly. Consult the textbook definition of append/3 for details. Or even better, look at the textbook definition of append/3 and adapt it to use if for "inserting".
There is a built-in predicate select/3 that does the same thing, although with the arguments in a different order.
Remember that (if defined correctly) a predicate can work in different directions. For instance, it can tell you what a list would look like after removing an element, it can (although it's fairly trivial) tell you what element to remove from one list to get another, or it can tell you what lists, after having a given element removed, would resemble a given list.
(Hint: you may want to look into that last one).

Prolog: take a list of two elements, return true if and only if the first element is same as second

I'm a newbie prolog programmer, and for an assignment, I have to have a basic program that succeeds if and only if list X is a list of two elements, with the first as the same as the second.
From my view of prolog, programs seem to be pretty small, so I typed this in:
firstPair(x,x).
When I run it under swipl, I get this as output:
Syntax error: Operator expected
Is there something more that needs to be done? I thought that if I executed this with say, firstPair(1,2). this would be all it would need to know that it is false.
First, lowercase x is not a variable, it's an atom. Make x uppercase to fix the problem:
firstPair(X,X).
Second, you do not type this into the interpreter. Rather, you write it into a file firstPair.pl, and then read that file into Prolog.
At the command prompt, type this:
['firstPair.pl'].
Press enter. Now you can use your firstPair/2 rule.
Finally, since the assignment talks about lists, I think the instructor wanted you to write firstPair/1, not firstPair/2:
firstPair([X,X]).
Your program/fact
firstPair(X,X).
will succeed if the two arguments given it can be unified, whether they are lists, atoms, variables, etc. To meet your specification, a
program that succeeds if and only if list X is a list of two elements,
with the first as the same as the second.
You need something like this:
list_of_two_elements( [X,X] ).
This will succeed if passed a single term that is (or can be unified with) a list of two elements that are, or can be made through unification, identical. For instance, all of the following will succeed:
list_of_two_elements( X ).
on success, the variable X will be unified with a list of two elements containing the same unbound variable, something like [V1,V1].
list_of_two_elements( [1,1] ).
list_of-two_elements( [1,X] ). (on success, X here will have been unified with the integer 1.)

Prolog: Difference of two lists

Pretty new to Prolog. I'm trying to give two lists and have the difference of the two returned to me. The second list can have bound variables and unbound variables in it. I've tried tracing this and it recurses all the way through and gives me a correct list in NewL, but then on the way back it negates all the deletes I've made. What is going wrong ? Thanks for the help!
% Find difference between two lists, return result in Difference
difference(List,[H|T],Difference) :- % When H is unbound var, use Tail
var(H),!,difference(List,T,Difference),!.
difference(List,[H|T],Difference) :- % When H is bound var, remove from List.
subtract(List,[H],NewL),
difference(NewL,T,Difference),!.
Assuming that subtract/3 comes from SWI-Prolog library, and thus is correct, you are left with just one possibility: you forgot to declare the base case on the second argument, that drives the recursion.
And take in account the comment from #mog, cuts should be used just when required. Of course, to decide when are required can be difficult...