I am trying to write a predicate palindrome/1 in Prolog that is true if and only if its list input consists of a palindromic list.
for example:
?- palindrome([1,2,3,4,5,4,3,2,1]).
is true.
Any ideas or solutions?
A palindrome list is a list which reads the same backwards, so you can reverse the list to check whether it yields the same list:
palindrome(L):-
reverse(L, L).
Looks that everybody is voting for a reverse/2 based solution. I guess you guys have a reverse/2 solution in mind that is O(n) of the given list. Something with an accumulator:
reverse(X,Y) :- reverse(X,[],Y).
reverse([],X,X).
reverse([X|Y],Z,T) :- reverse(Y,[X|Z],T).
But there are also other ways to check for a palindrome. I came up with a solution that makes use of DCG. One can use the following rules:
palin --> [].
palin --> [_].
palin --> [Border], palin, [Border].
Which solution is better? Well lets do some little statistics via the profile
command of the Prolog system. Here are the results:
So maybe the DCG solution is often faster in the positive case ("radar"), it does not
have to build the whole reverse list, but directly moves to the middle and then
checks the rest during leaving its own recursion. But disadvantage of DCG solution
is that it is non-deterministic. Some time measurements would tell more...
Bye
P.S.: Port statistics done with new plugable debugger of Jekejeke Prolog:
http://www.jekejeke.ch/idatab/doclet/prod/en/docs/10_dev/10_docu/02_reference/04_examples/02_count.html
But other Prolog systems have similar facilities. For more info see, "Code Profiler" column:
http://en.wikipedia.org/wiki/Comparison_of_Prolog_implementations
This sure sounds like a homework question, but I just can't help myself:
palindrome(X) :- reverse(X,X).
Technically, prolog functor don't "return" anything.
Another way, doing it with DCG's:
palindrome --> [_].
palindrome --> [C,C].
palindrome --> [C],palindrome,[C].
You can check for a palindrome like this:
?- phrase(palindrome,[a,b,a,b,a]).
true.
?- phrase(palindrome,[a,b,a,b,b]).
false.
You can use :
palindrome([]).
palindrome([_]).
palindrome([X|Xs]):-append(Xs1,[X],Xs), palindrome(Xs1).
Related
I am currently studying for an AI exam and my final thing to revise is Prolog.
Below you can see the question I have to solve:
If possible, unify uncle(brother(W), Q) with uncle(john, mother(S))
I am completely lost as on a high level, it doesn't unify, but I guess my knowledge of Prolog is a bit outdated.
Could someone help me understand how to unify them if possible? I don't understand how I would unify functors to atoms.
Thank you!
I find that when learning syntactic unification that it works better if you first decompose the terms into abstract syntax trees then do the unification. Doing the decomposition really helps with nested list. So after decomposition you have brother(W) trying to unify with john which will fail. Once you have a failure you can skip the rest of the unification process.
While these are not abstract syntax trees, the predicates =.. and write_term help in converting complex terms to ASTs.
Using =../2
?- uncle(brother(W),Q) =.. List.
List = [uncle, brother(W), Q].
?- uncle(john,mother(S)) =.. List.
List = [uncle, john, mother(S)].
and then trying to unify the individual terms
?- brother(W) = john.
false.
For Prolog list use write_term/2 with option dotlists(true)
?- write_term([a],[dotlists(true)]).
.(a,[])
true.
?- write_term([a,b],[dotlists(true)]).
.(a,.(b,[]))
true.
?- write_term([a,B],[dotlists(true)]).
.(a,.(_15276,[]))
true.
?- write_term([a,[b]],[dotlists(true)]).
.(a,.(.(b,[]),[]))
true.
?- write_term([a,b,[c],[d,[e,[f],g]],h],[dotlists(true)]).
.(a,.(b,.(.(c,[]),.(.(d,.(.(e,.(.(f,[]),.(g,[]))),[])),.(h,[])))))
true.
In this post are some actual ASTs for learning syntactic unification.
Do to limitation of Discourse I could not get the images to line up with the actual Prolog terms as I wanted but it is better than not having them.
I am attempting to learn basic Prolog. I have read some basic tutorials on the basic structures of lists, variables, and if/and logic. A project I am attempting to do to help learn some of this is to match DNA sequences.
Essentially I want it to match reverse compliments of DNA sequences.
Example outputs can be seen below:
?- dnamatch([t, t, a, c],[g, t, a, a]).
true
While it's most likely relatively simple, being newer to Prolog I am currently figuring it out.
I started by defining basic matching rules for the DNA pairs:
pair(a,t).
pair(g,c).
etc...
I was then going to try to implement this into lists somehow, but am unsure how to make this logic apply to longer lists of sequences. I am unsure if my attempted start is even the correct approach. Any help would be appreciated.
Since your relation is describing lists, you could opt to use DCGs. You can describe the complementary nucleobases like so:
complementary(t) --> % thymine is complementary to
[a]. % adenine
complementary(a) --> % adenine is complementary to
[t]. % thymine
complementary(g) --> % guanine is complementary to
[c]. % cytosine
complementary(c) --> % cytosine is complementary to
[g]. % guanine
This corresponds to your predicate pair/2. To describe a bonding sequence in reverse order you can proceed like so:
bond([]) --> % the empty sequence
[]. % doesn't bond
bond([A|As]) --> % the sequence [A|As] bonds with
bond(As), % a bonding sequence to As (in reverse order)
complementary(A). % followed by the complementary nucleobase of A
The reverse order is achieved by writing the recursive goal first and then the goal that describes the complementary nucleobase to the one in the head of the list. You can query this using phrase/2 like so:
?- phrase(bond([t,t,a,c]),S).
S = [g,t,a,a]
Or you can use a wrapper predicate with a single goal containing phrase/2:
seq_complseq(D,M) :-
phrase(bond(D),M).
And then query it:
?- seq_complseq([t,t,a,c],C).
C = [g,t,a,a]
I find the description of lists with DCGs easier to read than the corresponding predicate version. Of course, describing a complementary sequence in reverse order is a relatively easy task. But once you want to describe more complex structures like, say the cloverleaf structure of tRNA DCGs come in real handy.
A solution with maplist/3 and reverse/2:
dnamatch(A,B) :- reverse(B,C), maplist(pairmatch,A,C).
If you want to avoid traversing twice you can also maybe do it like this?
rev_comp(DNA, RC) :-
rev_comp(DNA, [], RC).
rev_comp([], RC, RC).
rev_comp([X|Xs], RC0, RC) :-
pair(X, Y),
rev_comp(Xs, [Y|RC0], RC).
Then:
?- rev_comp([t,c,g,a], RC).
RC = [t, c, g, a].
This is only hand-coded amalgamation of reverse and maplist. Is it worth it? Maybe, maybe not. Probably not.
Now that I thought about it a little bit, you could also do it with foldl which reverses, but now you really want to reverse so it is more useful than annoying.
rev_comp([], []).
rev_comp([X|Xs], Ys) :-
pair(X, Y),
foldl(rc, Xs, [Y], Ys).
rc(X, Ys, [Y|Ys]) :- pair(X, Y).
But this is even less obvious than solution above and solution above is still less obvious than solution by #Capellic so maybe you can look at code I wrote but please don't write such code unless of course you are answering questions of Stackoverflow and want to look clever or impress a girl that asks your help for exercise in university.
This is probably the most trivial implementation of a function that returns the length of a list in Prolog
count([], 0).
count([_|B], T) :- count(B, U), T is U + 1.
one thing about Prolog that I still cannot wrap my head around is the flexibility of using variables as parameters.
So for example I can run count([a, b, c], 3). and get true. I can also run count([a, b], X). and get an answer X = 2.. Oddly (at least for me) is that I can also run count(X, 3). and get at least one result, which looks something like X = [_G4337877, _G4337880, _G4337883] ; before the interpreter disappears into an infinite loop. I can even run something truly "flexible" like count(X, A). and get X = [], A = 0 ; X = [_G4369400], A = 1., which is obviously incomplete but somehow really nice.
Therefore my multifaceted question. Can I somehow explain to Prolog not to look beyond first result when executing count(X, 3).? Can I somehow make Prolog generate any number of solutions for count(X, A).? Is there a limitation of what kind of solutions I can generate? What is it about this specific predicate, that prevents me from generating all solutions for all possible kinds of queries?
This is probably the most trivial implementation
Depends from viewpoint: consider
count(L,C) :- length(L,C).
Shorter and functional. And this one also works for your use case.
edit
library CLP(FD) allows for
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :- U #>= 0, T #= U + 1, count(B, U).
?- count(X,3).
X = [_G2327, _G2498, _G2669] ;
false.
(further) answering to comments
It was clearly sarcasm
No, sorry for giving this impression. It was an attempt to give you a synthetic answer to your question. Every details of the implementation of length/2 - indeed much longer than your code - have been carefully weighted to give us a general and efficient building block.
There must be some general concept
I would call (full) Prolog such general concept. From the very start, Prolog requires us to solve computational tasks describing relations among predicate arguments. Once we have described our relations, we can query our 'knowledge database', and Prolog attempts to enumerate all answers, in a specific order.
High level concepts like unification and depth first search (backtracking) are keys in this model.
Now, I think you're looking for second order constructs like var/1, that allow us to reason about our predicates. Such constructs cannot be written in (pure) Prolog, and a growing school of thinking requires to avoid them, because are rather difficult to use. So I posted an alternative using CLP(FD), that effectively shields us in some situation. In this question specific context, it actually give us a simple and elegant solution.
I am not trying to re-implement length
Well, I'm aware of this, but since count/2 aliases length/2, why not study the reference model ? ( see source on SWI-Prolog site )
The answer you get for the query count(X,3) is actually not odd at all. You are asking which lists have a length of 3. And you get a list with 3 elements. The infinite loop appears because the variables B and U in the first goal of your recursive rule are unbound. You don't have anything before that goal that could fail. So it is always possible to follow the recursion. In the version of CapelliC you have 2 goals in the second rule before the recursion that fail if the second argument is smaller than 1. Maybe it becomes clearer if you consider this slightly altered version:
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :-
T #> 0,
U #= T - 1,
count(B, U).
Your query
?- count(X,3).
will not match the first rule but the second one and continue recursively until the second argument is 0. At that point the first rule will match and yield the result:
X = [_A,_B,_C] ?
The head of the second rule will also match but its first goal will fail because T=0:
X = [_A,_B,_C] ? ;
no
In your above version however Prolog will try the recursive goal of the second rule because of the unbound variables B and U and hence loop infinitely.
Recall this proof meta-circular
solve(true, true).
solve([], []).
solve([A|B],[ProofA|ProofB]) :-
solve(A,ProofA),
solve(B, ProofB).
solve(A, node(A,Proof)) :-
rule(A,B),
solve(B,Proof).
Assume that the third rule of the interpreter is altered, while the other rules of the interpreter are unchanged, as follows:
% Signature: solve(Exp, Proof)/2 solve(true, true).
solve([], []).
solve([A|B], [ProofA|ProofB]) :-
solve(B, ProofB), %3
solve(A, ProofA).
solve(A, node(A, Proof)) :-
rule(A, B),
solve(B, Proof).
Consider the proof tree that will be created for some query in both versions. Can any variable substitution be achieved in one version only? Explain. Can any true leaf move to the other side of the most left infinite branch? Explain. In both questions give an example if the answer is positive. How will this influence on the proof?
please help me ! tx
(I have a lot of reservations against your meta-interpreter. But first I will answer the question you had)
In this meta-interpreter you are reifying (~ implementing) conjunction. And you implement it with Prolog's conjunction. Now you have two different versions how you interpret a conjunction. Once you say prove A first, then B. Then you say the opposite.
Think of
p :- p, false.
and
p :- false, p.
The second version will produce a finite failure branch, whereas the first will produce an infinite failure branch. So that will be the effect of using one or the other meta-interpreter. Note that this "error" might again be mitigated by interpreting the meta-interpreter itself!
See also this answer which might clarify the notions a bit.
There are also other ways to implement conjunction (via binarization) ; such that the next level of meta-interpreter will no longer able to compensate.
Finally a comment on the style of your meta-interpreter: You are mixing lists and other terms. In fact [true|true] will be true. Avoid such a representation by all means. Either stick with the traditional "vanilla" representation which operates on the syntax tree of Prolog rules. That is, conjunction is represented as (',')/2. Or stick to lists. But do not mix lists and other representations.
I'm new to Prolog and I'm trying to resolve this exercise:
Define a predicate greater_than/2 that takes two numerals in the notation that we introduced in this lecture (i.e. 0, succ(0), succ(succ(0))...) as arguments and decides whether the first one is greater than the second one. E.g:
?- greater_than( succ(succ(succ(0))), succ(0) ).
yes.
?- greater_than( succ(succ(0)), succ(succ(succ(0))) ).
no.
This is my answer so far:
greater_than(X, 0).
greater_than( succ(succ(X)), succ(Y) ).
but of course doesn't work properly, so I'm asking anyone for help.
Thanks.
As you are looking for a recursive solution, you have to provide a base case and a recursive step.
The base case you provided is almost right. However it fails because it will succeed for example when both numbers are zero. It should succeed only when the left side is of the form succ(_) and the right side is zero.
The recursive step should take an element from each side and apply recursion.
Therefore this should work:
greater_than(succ(_), 0).
greater_than(succ(X), succ(Y)):-
greater_than(X, Y).