So I have this mathematical language, it goes like this:
E -> number
[+,E,E,E] //e.g. [+,1,2,3] is 1+2+3 %we can put 2 to infinite Es here.
[-,E,E,E] //e.g. [-,1,2,3] is 1-2-3 %we can put 2 to infinite Es here.
[*,E,E,E] //e.g. [*,1,2,3] is 1*2*3 %we can put 2 to infinite Es here.
[^,E,E] //e.g. [^,2,3] is 2^3
[sin,E] //e.g. [sin,0] is sin 0
[cos,E] //e.g. [cos,0] is cos 0
and I want to write the set of rules that finds the numeric value of a mathematical expression written by this language in prolog.
I first wrote a function called "check", it checks to see if the list is written in a right way according to the language we have :
check1([]).
check1([L|Ls]):- number(L),check1(Ls).
check([L|Ls]):-atom(L),check1(Ls).
now I need to write the function "evaluate" that takes a list that is an expression written by this language, and a variable that is the numeric value corresponding to this language.
example:
?-evaluate([*,1,[^,2,2],[*,2,[+,[sin,0],5]]]],N) -> N = 40
so I wrote this:
sum([],0).
sum([L|Ls],N):- not(is_list(L)),sum(Ls,No),N is No + L.
min([],0).
min([L|Ls],N):-not(is_list(L)), min(Ls,No),N is No - L.
pro([],0).
pro([X],[X]).
pro([L|Ls],N):-not(is_list(L)), pro(Ls,No), N is No * L.
pow([L|Ls],N):-not(is_list(L)), N is L ^ Ls.
sin_(L,N):-not(is_list(L)), N is sin(L).
cos_(L,N):-not(is_list(L)), N is cos(L).
d([],0).
d([L|Ls],N):- L == '+' ,sum(Ls,N);
L == '-',min(Ls,N);
L == '*',pro(Ls,N);
L == '^',pow(Ls,N);
L == 'sin',sin_(Ls,N);
L == 'cos',cos_(Ls,N).
evaluate([],0).
evaluate([L|Ls],N):-
is_list(L) , check(L) , d(L,N),L is N,evaluate(Ls,N);
is_list(L), not(check(L)) , evaluate(Ls,N);
not(is_list(L)),not(is_list(Ls)),check([L|Ls]),d([L|Ls],N),
L is N,evaluate(Ls,N);
is_list(Ls),evaluate(Ls,N).
and it's working for just a list and returning the right answer , but not for multiple lists inside the main list, how should my code be?
The specification you work with looks like a production rule that describes that E (presumably short for Expression) might be a number or one of the 6 specified operations. That is the empty list [] is not an expression. So the fact
evaluate([],0).
should not be in your code. Your predicate sum/2 almost works the way you wrote it, except for the empty list and a list with a single element, that are not valid inputs according to your specification. But the predicates min/2 and pro/2 are not correct. Consider the following examples:
?- sum([1,2,3],X).
X = 6 % <- correct
?- sum([1],X).
X = 1 % <- incorrect
?- sum([],X).
X = 0 % <- incorrect
?- min([1,2,3],X).
X = -6 % <- incorrect
?- pro([1,2,3],X).
X = 6 ? ; % <- correct
X = 0 % <- incorrect
Mathematically speaking, addition and multiplication are associative but subtraction is not. In programming languages all three of these operations are usually left associative (see e.g. Operator associativity) to yield the mathematically correct result. That is, the sequence of subtractions in the above query would be calculated:
1-2-3 = (1-2)-3 = -4
The way you define a sequence of these operations resembles the following calculation:
[A,B,C]: ((0 op C) op B) op A
That works out fine for addition:
[1,2,3]: ((0 + 3) + 2) + 1 = 6
But it doesn't for subtraction:
[1,2,3]: ((0 - 3) - 2) - 1 = -6
And it is responsible for the second, incorrect solution when multiplying:
[1,2,3]: ((0 * 3) * 2) * 1 = 0
There are also some other issues with your code (see e.g. #lurker's comments), however, I won't go into further detail on that. Instead, I suggest a predicate that adheres closely to the specifying production rule. Since the grammar is describing expressions and you want to know the corresponding values, let's call it expr_val/2. Now let's describe top-down what an expression can be: It can be a number:
expr_val(X,X) :-
number(X).
It can be an arbitrarily long sequence of additions or subtractions or multiplications respectively. For the reasons above all three sequences should be evaluated in a left associative way. So it's tempting to use one rule for all of them:
expr_val([Op|Es],V) :-
sequenceoperator(Op), % Op is one of the 3 operations
exprseq_op_val(Es,Op,V). % V is the result of a sequence of Ops
The power function is given as a list with three elements, the first being ^ and the others being expressions. So that rule is pretty straightforward:
expr_val([^,E1,E2],V) :-
expr_val(E1,V1),
expr_val(E2,V2),
V is V1^V2.
The expressions for sine and cosine are both lists with two elements, the first being sin or cos and the second being an expression. Note that the argument of sin and cos is the angle in radians. If the second argument of the list yields the angle in radians you can use sin/1 and cos/2 as you did in your code. However, if you get the angle in degrees, you need to convert it to radians first. I include the latter case as an example, use the one that fits your application.
expr_val([sin,E],V) :-
expr_val(E,V1),
V is sin(V1*pi/180). % radians = degrees*pi/180
expr_val([cos,E],V) :-
expr_val(E,V1),
V is cos(V1*pi/180). % radians = degrees*pi/180
For the second rule of expr_val/2 you need to define the three possible sequence operators:
sequenceoperator(+).
sequenceoperator(-).
sequenceoperator(*).
And subsequently the predicate exprseq_op_val/3. As the leading operator has already been removed from the list in expr_val/2, the list has to have at least two elements according to your specification. In order to evaluate the sequence in a left associative way the value of the head of the list is passed as an accumulator to another predicate exprseq_op_val_/4
exprseq_op_val([E1,E2|Es],Op,V) :-
expr_val(E1,V1),
exprseq_op_val_([E2|Es],Op,V,V1).
that is describing the actual evaluation. There are basically two cases: If the list is empty then, regardless of the operator, the accumulator holds the result. Otherwise the list has at least one element. In that case another predicate, op_val_args/4, delivers the result of the respective operation (Acc1) that is then recursively passed as an accumulator to exprseq_op_val_/4 alongside with the tail of the list (Es):
exprseq_op_val_([],_Op,V,V).
exprseq_op_val_([E1|Es],Op,V,Acc0) :-
expr_val(E1,V1),
op_val_args(Op,Acc1,Acc0,V1),
exprseq_op_val_(Es,Op,V,Acc1).
At last you have to define op_val_args/4, that is again pretty straightforward:
op_val_args(+,V,V1,V2) :-
V is V1+V2.
op_val_args(-,V,V1,V2) :-
V is V1-V2.
op_val_args(*,V,V1,V2) :-
V is V1*V2.
Now let's see how this works. First your example query:
?- expr_val([*,1,[^,2,2],[*,2,[+,[sin,0],5]]],V).
V = 40.0 ? ;
no
The simplest expression according to your specification is a number:
?- expr_val(-3.14,V).
V = -3.14 ? ;
no
The empty list is not an expression:
?- expr_val([],V).
no
The operators +, - and * need at least 2 arguments:
?- expr_val([-],V).
no
?- expr_val([+,1],V).
no
?- expr_val([*,1,2],V).
V = 2 ? ;
no
?- expr_val([-,1,2,3],V).
V = -4 ? ;
no
The power function has exactly two arguments:
?- expr_val([^,1,2,3],V).
no
?- expr_val([^,2,3],V).
V = 8 ? ;
no
?- expr_val([^,2],V).
no
?- expr_val([^],V).
no
And so on...
here is the plus code that i don't understand
plus(0,X,X):-natural_number(X).
plus(s(X),Y,s(Z)) :- plus(X,Y,Z).
while given :
natural_number(0).
natural_number(s(X)) :- natural_number(X).
I don't understand this recursion. If I have plus(s(0),s(s(s(0))),Z) how can i get the answer of 1+3=4?
I need some explanation for the first code. I try that plus(0,X,X) will stop the recursion but I think that I do it wrong.
So, let's start with natural_number(P). Read this as "P is a natural number". We're given natural_number(0)., which tells us that 0 is always a natural number (i.e. there are no conditions that must be met for it to be a fact). natural_number(s(X)) :- natural_number(X). tells us that s(X) is a natural number if X is a natural number. This is the normal inductive definition of natural numbers, but written "backwards" as we read Prolog "Q := P" as "Q is true if P is true".
Now we can look at plus(P, Q, R). Read this as "plus is true if P plus Q equals R". We then look at the cases we're given:
plus(0,X,X) :- natural_number(X).. Read as Adding 0 to X results in X if X is a natural number. This is our inductive base case, and is the natural definition of addition.
plus(s(X),Y,s(Z)) :- plus(X,Y,Z). Read as "Adding the successor of X to Y results in the successor Z if adding X to Y is Z'. If we change the notation, we can read it algebraically as "X + 1 + Y = Z + 1 if X + Y = Z", which is very natural again.
So, to answer you direct question "If I have plus(s(0),s(s(s(0))),z), how can i get the answer of 1+3=4?", let's consider how we can unify something with z each step of the induction
Apply the second definition of plus, as it's the only one that unifies with the query. plus(s(0),s(s(s(0))), s(z')) is true if plus(0, s(s(s(0))), z') is true for some z
Now apply the first definition of plus, as it's the only unifying definition: plus(0, s(s(s(0))), z') if z' is s(s(s(0))) and s(s(s(0))) is a natural number.
Unwind the definition of natural_number a few times on s(s(s(0))) to see that is true.
So the overall statement is true, if s(s(s(0))) is unified with z' and s(z') is unified with z.
So the interpreter returns true, with z' = s(s(s(0))) and z = s(z'), i.e. z = s(s(s(s(0)))). So, z is 4.
That code is a straightforward implementation of addition in Peano arithmetic.
In Peano arithmetic, natural numbers are represented using the constant 0 and the unary function s. So s(0) is a representation of 1, s(s(s(0))) is representation of 3. And plus(s(0),s(s(s(0))),Z) will give you Z = s(s(s(s(0)))), which is a representation of 4.
You won't get numerical terms like 1+3=4, all you get is the term s/1 which can embed itself to any depth and thus can represent any natural number. You can combine such terms (using plus/3) and thereby achieve summing.
Note that your definition of plus/3 has nothing to do with SWI-Prolog's built-in plus/3 (which works with integers and not with the s/1 terms):
?- help(plus).
plus(?Int1, ?Int2, ?Int3)
True if Int3 = Int1 + Int2.
At least two of the three arguments must be instantiated to integers.
I'm trying to learn SWI prolog,
but my simple program fails when I believe it should succeed.
%My code:
orthogonal((X1,Y1,Z1),(X2,Y2,Z2)) :- (X1*X2)+(Y1*Y2)+(Z1*Z2)==0.
integerVector((X,Y,Z)) :- integer(X),integer(Y),integer(Z).
?-orthogonal((1,0,0),(0,0,1)).
I press compile Buffer in the pseudoemacs window and the output is:
% [PATH].pl compiled 0.00 sec, 136 bytes
ERROR: emacs_prolog_mode ->error_at_location: Argument 1 (int): `int' expected, found `#11470948?start'
ERROR: emacs_prolog_mode ->error_at_location: Argument 1 (int): `int' expected, found `#11470948?start'
Warning: [PATH]s.pl:5:
Goal (directive) failed: user:orthogonal((1,0,0), (0,0,1))
You have used (==)/2 in place of (=:=)/2 which evaluates its arguments as arithmetic expressions.
You can use (X,Y,Z), but it isn't a triple as in e.g. Haskell. To see this:
?- write_canonical((1,2,3)).
','(1,','(2,3))
?- (1,2,3) = (X,Y).
X = 1, Y = (2,3).
Expressions in Prolog simply represent syntactic term trees. To evaluate an expression you need to X is Y which evaluates Y as an arithmetic expression and unifies the result with X. Alternatively you can use X =:= Y which evaluates both X and Y as arithmetic expressions, then unifies the results.
Cheers!