Evaluating an algebraic expression - prolog

This is a test review question that I am having trouble with. How do you write a method to evaluate an algebraic expression with the operators plus,
minus and times. Here are some test queries:
simplify(Expression, Result, List)
?- simplify(plus(times(x,y),times(3 ,minus(x,y))),V,[x:4,y:2]).
V = 14
?- simplify(times(2,plus(a,b)),Val,[a:1,b:5]).
Val = 12
?- simplify(times(2,plus(a,b)),Val,[a:1,b:(-5)]).
Val = -8 .
All I was given were these sample queries and no other explanation. But I am pretty sure the method is supposed to dissect the first argument, which is the algebraic expression, substituting x and y for their values in the 3rd argument (List). The second argument should be the result after evaluating the expression.
I think one of the methods should be simplify(V, Val, L) :- member(V:Val, L).
Ideally there should only be 4 more methods... but I'm not sure how to go about this.

Start small, write down what you know.
simplify(plus(times(x,y),times(3 ,minus(x,y))),V,[x:4,y:2]):- V = 14.
is a perfectly good start: (+ (* 4 2) (* 3 (- 4 2))) = 8 + 3*2 = 14. But then, of course,
simplify(times(x,y),V,[x:4,y:2]):- V is 4*2.
is even better. Also,
simplify(minus(x,y),V,[x:4,y:2]):- V is 4-2.
simplify(plus(x,y),V,[x:4,y:2]):- V is 4+2.
simplify(x,V,[x:4,y:2]):- V is 4.
all perfectly good Prolog code. But of course what we really mean, it becomes apparent, is
simplify(A,V,L):- atom(A), getVal(A,L,V).
simplify(C,V,L):- compound(C), C =.. [F|T],
maplist( simp(L), T, VS), % get the values of subterms
calculate( F, VS, V). % calculate the final result
simp(L,A,V):- simplify(A,V,L). % just a different args order
etc. getVal/3 will need to retrieve the values somehow from the L list, and calculate/3 to actually perform the calculation, given a symbolic operation name and the list of calculated values.
Study maplist/3 and =../2.
(not finished, not tested).
OK, maplist was an overkill, as was =..: all your terms will probably be of the form op(A,B). So the definition can be simplified to
simplify(plus(A,B),V,L):-
simplify(A,V1,L),
simplify(B,V2,L),
V is V1 + V2. % we add, for plus
simplify(minus(A,B),V,L):-
% fill in the blanks
.....
V is V1 - V2. % we subtract, for minus
simplify(times(A,B),V,L):-
% fill in the blanks
.....
V is .... . % for times we ...
simplify(A,V,L):-
number(A),
V = .... . % if A is a number, then the answer is ...
and the last possibility is, x or y etc., that satisfy atom/1.
simplify(A,V,L):-
atom(A),
retrieve(A,V,L).
So the last call from the above clause could look like retrieve(x,V,[x:4, y:3]), or it could look like retrieve(y,V,[x:4, y:3]). It should be a straightforward affair to implement.

Related

Defining a mathematical language in prolog

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...

How can I verify if a coordinate is in a list

I'm generating random coordinates and adding on my list, but first I need verify if that coordinate already exists. I'm trying to use member but when I was debugging I saw that isn't working:
My code is basically this:
% L is a list and Q is a count that define the number of coordinate
% X and Y are the coordinate members
% check if the coordniate already exists
% if exists, R is 0 and if not, R is 1
createCoordinates(L,Q) :-
random(1,10,X),
random(1,10,Y),
convertNumber(X,Z),
checkCoordinate([Z,Y],L,R),
(R is 0 -> print('member'), createCoordinates(L,Q); print('not member'),createCoordinates(L,Q-1).
checkCoordinate(C,L,R) :-
(member(C,L) -> R is 0; R is 1).
% transforms the number N in a letter L
convertNumber(N,L) :-
N is 1, L = 'A';
N is 2, L = 'B';
...
N is 10, L = 'J'.
%call createCoordinates
createCoordinates(L,20).
When I was debugging this was the output:
In this picture I'm in the firts interation and L is empty, so R should be 1 but always is 0, the coordinate always is part of the list.
I have the impression that the member clause is adding the coordinate at my list and does'nt make sense
First off, I would recommend breaking your problem down into smaller pieces. You should have a procedure for making a random coordinate:
random_coordinate([X,Y]) :-
random(1, 10, XN), convertNumber(XN, X),
random(1, 10, Y).
Second, your checkCoordinate/3 is converting Prolog's success/failure into an integer, which is just busy work for Prolog and not really improving life for you. memberchk/2 is completely sufficient to your task (member/2 would work too but is more powerful than necessary). The real problem here is not that member/2 didn't work, it's that you are trying to build up this list parameter on the way out, but you need it to exist on the way in to examine it.
We usually solve this kind of problem in Prolog by adding a third parameter and prepending values to the list on the way through. The base case then equates that list with the outbound list and we protect the whole thing with a lower-arity procedure. In other words, we do this:
random_coordinates(N, Coordinates) :- random_coordinates(N, [], Coordinates).
random_coordinates(0, Result, Result).
random_coordinates(N, CoordinatesSoFar, FinalResult) :- ...
Now that we have two things, memberchk/2 should work the way we need it to:
random_coordinates(N, CoordinatesSoFar, FinalResult) :-
N > 0, succ(N0, N), % count down, will need for recursive call
random_coordinate(Coord),
(memberchk(Coord, CoordinatesSoFar) ->
random_coordinates(N, CoordinatesSoFar, FinalResult)
;
random_coordinates(N0, [Coord|CoordinatesSoFar], FinalResult)
).
And this seems to do what we want:
?- random_coordinates(10, L), write(L), nl.
[[G,7],[G,3],[H,9],[H,8],[A,4],[G,1],[I,9],[H,6],[E,5],[G,8]]
?- random_coordinates(10, L), write(L), nl.
[[F,1],[I,8],[H,4],[I,1],[D,3],[I,6],[E,9],[D,1],[C,5],[F,8]]
Finally, I note you continue to use this syntax: N is 1, .... I caution you that this looks like an error to me because there is no distinction between this and N = 1, and your predicate could be stated somewhat tiresomely just with this:
convertNumber(1, 'A').
convertNumber(2, 'B').
...
My inclination would be to do it computationally with char_code/2 but this construction is actually probably better.
Another hint that you are doing something wrong is that the parameter L to createCoordinates/2 gets passed along in all cases and is not examined in any of them. In Prolog, we often have variables that appear to just be passed around meaninglessly, but they usually change positions or are used multiple times, as in random_coordinates(0, Result, Result); while nothing appears to be happening there, what's actually happening is plumbing: the built-up parameter becomes the result value. Nothing interesting is happening to the variable directly there, but it is being plumbed around. But nothing is happening at all to L in your code, except it is supposedly being checked for a new coordinate. But you're never actually appending anything to it, so there's no reason to expect that anything would wind up in L.
Edit Notice that #lambda.xy.x solves the problem in their answer by prepending the new coordinate in the head of the clause and examining the list only after the recursive call in the body, obviating the need for the second list parameter.
Edit 2 Also take a look at #lambda.xy.x's other solution as it has better time complexity as N approaches 100.
Since i had already written it, here is an alternative solution: The building block is gen_coord_notin/2 which guarantees a fresh solution C with regard to an exclusion list Excl.
gen_coord_notin(C, Excl) :-
random(1,10,X),
random(1,10,Y),
( memberchk(X-Y, Excl) ->
gen_coord_notin(C, Excl)
;
C = X-Y
).
The trick is that we only unify C with the new result, if it is fresh.
Then we only have to fold the generations into N iterations:
gen_coords([], 0).
gen_coords([X|Xs], N) :-
N > 0,
M is N - 1,
gen_coords(Xs, M),
gen_coord_notin(X, Xs).
Remark 1: since coordinates are always 2-tuples, a list representation invites unwanted errors (e.g. writing [X|Y] instead of [X,Y]). Traditionally, an infix operator like - is used to seperate tuples, but it's not any different than using coord(X,Y).
Remark 2: this predicate is inherently non-logical (i.e. calling gen_coords(X, 20) twice will result in different substitutions for X). You might use the meta-level predicates var/1, nonvar/1, ground/1, integer, etc. to guard against non-sensical calls like gen_coord(1-2, [1-1]).
Remark 3: it is also important that the conditional does not have multiple solutions (compare member(X,[A,B]) and memberchk(X,[A,B])). In general, this can be achieved by calling once/1 but there is a specialized predicate memberchk/2 which I used here.
I just realized that the performance of my other solutions is very bad for N close to 100. The reason is that with diminishing possible coordinates, the generate and test approach will take longer and longer. There's an alternative solution which generates all coordinates and picks N random ones:
all_pairs(Ls) :-
findall(X-Y, (between(1,10,X), between(1,10,Y)), Ls).
remove_index(X,[X|Xs],Xs,0).
remove_index(I,[X|Xs],[X|Rest],N) :-
N > 0,
M is N - 1,
remove_index(I,Xs,Rest,M).
n_from_pool(_Pool, [], 0).
n_from_pool(Pool, [C|Cs], N) :-
N > 0,
M is N - 1,
length(Pool, L),
random(0,L,R),
remove_index(C,Pool,NPool,R),
n_from_pool(NPool, Cs, M).
gen_coords2(Xs, N) :-
all_pairs(Pool),
n_from_pool(Pool, Xs, N).
Now the query
?- gen_coords2(Xs, 100).
Xs = [4-6, 5-6, 5-8, 9-6, 3-1, 1-3, 9-4, 6-1, ... - ...|...] ;
false.
succeeds as expected. The error message
?- gen_coords2(Xs, 101).
ERROR: random/1: Domain error: not_less_than_one' expected, found0'
when we try to generate more distinct elements than possible is not nice, but better than non-termination.

PROLOG Logic Programm - Addition of 2x2 matrix

I am new to PROLOG and am trying some simple exercises to familiarize myself with it. However I am stuck in making an addition of 2x2matrix with another, more specifically lists within lists.
This is my code, the output using SWI-Prolog is False, and I have no idea why. Any help is appreciated!
matrixAdd([X],[Y],[S]) :- S is X + Y.
matrixAdd([[H|A],[I|B]],[[J|C],[K|D]],[[S1|Sum1],[S2|Sum2]]) :-
S1 = H + J,
S2 = I + K,
matrixAdd([A,B],[C,D],[Sum1,Sum2]).
Elaborating:
?- A = 2 + 3.
A = 2+3.
?- A = 2 + 3, A == 5.
false.
?- A = 2 + 3, A = 5.
false.
?- A is 2 + 3, A =:= 10/2.
A = 5.
?- A is 2 + 3, A = 10/2.
false.
Figure out why you get each of these answers.
Furthermore, think about how you want to represent your matrix. Does it need to be a nested list? For example, it could be something like matrix(dim(2,2), [1,2,3,4]). Then, adding two matrices would be as easy as:
matrix_sum(matrix(D, V1), matrix(D, V2), matrix(D, Sum)) :-
maplist(add, V1, V2, Sum).
add(X, Y, Sum) :-
Sum is X + Y.
(You could get fancy and use a constraint library for the add operation. For example, with library(clpr) you could write {Sum = X+Y} and use the same predicate for addition and for subtraction.)
This uses unification in the head to make sure that the two matrices have the same dimensions, while the maplist take care of V1 and V2 being the same length.
Or you prefer a list of lists. Then, figure out the general predicate that adds lists of lists together (see the other answer!). Now you have a weird mix where you kind of know the magnitude of one dimension in advance, but still attempt to traverse the other dimension. As your code is at the moment, it is your base case that always fails. It should be:
matrixAdd([[],[]],[[],[]],[[],[]]).
(so many lists!) and without any body. Try replacing it in your original code and see what happens.
You first need to know the following:
There is a difference between = and is/2
Check your list syntax [Head|Tail] is different from [Element1,Element2]
Unification of [X] will only work when you pass a list with exactly 1 element. Just like [[A,B],[C,D]] will only match a 2 by 2 matrix. (Note that the elements A,B,.. could be lists as well in that case)
For you hardcode solution, fixing all these issues should work, but I want to leave that to you for now.
matrixAddHardcode([[A1,A2],[A3,A4]],[[B1,B2],[B3,B4]],[[S1,S2],[S3,S4]]) :-
S1 is A1 + B1,
S2 is A2 + B2,
S3 is A3 + B3,
S4 is A4 + B4.
Solution for any X by Y
matrixAddFix([],[],[]).
matrixAddFix([L1|T1],[L2|T2],[S1|TS]) :-
listSum(L1,L2,S1),
matrixAddFix(T1,T2,TS).
listSum([],[],[]).
listSum([H1|T1],[H2|T2],[S1|TS]) :-
S1 is H1+H2,
listSum(T1,T2,TS).

Sum of the first n numbers in prolog

Hello can anyone help me compute the sum of the first n numbers. For example n=4 => sum = 10.
So far I've wrote this
predicates
sum(integer,integer)
clauses
sum(0,0).
sum(N,R):-
N1=N-1,
sum(N1,R1),
R=R1+N.
This one works but I need another implementation. I don't have any ideas how I could make this differen . Please help
What #mbratch said.
What you're computing is a triangular number. If your homework is about triangular numbers and not about learning recursive thinking, you can simply compute it thus:
triangular_number(N,R) :- R is N * (N+1) / 2 .
If, as is more likely, you're learning recursive thought, try this:
sum(N,R) :- % to compute the triangular number n,
sum(N,1,0,R) % - invoke the worker predicate with its counter and accumulator properly seeded
.
sum(0,_,R,R). % when the count gets decremented to zero, we're done. Unify the accumulator with the result.
sum(C,X,T,R) :- % otherwise,
C > 0 , % - assuming the count is greater than zero
T1 is T+X , % - increment the accumulator
X1 is X+1 , % - increment the current number
C1 is C-1 , % - decrement the count
sum(C1,X1,T1,R) % - recurse down
. % Easy!
Edited to add:
Or, if you prefer a count down approach:
sum(N,R) :- sum(N,0,R).
sum(0,R,R). % when the count gets decremented to zero, we're done. Unify the accumulator with the result.
sum(N,T,R) :- % otherwise,
N > 0 , % - assuming the count is greater than zero
T1 is T+N , % - increment the accumulator
N1 is N-1 , % - decrement the count
sum(N1,T1,R) % - recurse down
. % Easy!
Both of these are tail-recursive, meaning that the prolog compiler can turn them into iteration (google "tail recursion optimization" for details).
If you want to eliminate the accumulator, you need to do something like this:
sum(0,0).
sum(N,R) :-
N > 0 ,
N1 is N-1 ,
sum(N1,R1) ,
R is R1+N
.
A little bit simpler, but each recursion consumes another stack frame: given a sufficiently large value for N, execution will fail with a stack overflow.
sum(N, Sum) :-
Sum is (N + 1) * N / 2 .
Since you already got plenty of advice about your code, let me throw in a snippet (a bit off-topic).
Counting, and more generally, aggregating, it's an area where Prolog doesn't shine when compared to other relational,declarative languages (read SQL). But some vendor specific library make it much more pleasant:
?- aggregate(sum(N),between(1,4,N),S).
S = 10.
This is the "heart" of your program:
sum(N,R):-
R=R+N,
N=N-1,
sum(N,R).
The =/2 predicate (note the /2 means it accepts 2 arguments) is the instantiation predicate, not an assignment, or logical equal. It attempts to unify its arguments to make them the same. So if N is anything but 0, then R=R+N will always fail because R can never be the same as R+N. Likewise for N=N-1: it will always fail because N and N-1 can never be the same.
In the case of =/2 (unification), expressions are not evaluated. They are just terms. So if Y = 1, then X = Y + 1 unifies X with 1+1 as a term (equivalently written +(1,1)).
Because of the above issues, sum will always fail.
Numerical assignment of an arithmetic expression is done in Prolog with the is/2 predicate. Like this:
X is Y + 1.
This operator unifies the value of X to be the same as the value of the evaluated expression Y+1. In this case, you also cannot have X is X+1 for the same reason given above: X cannot be made the same as X+1 and Prolog does not allow "re-instantiation" of a variable inside of a clause. So you would need something like, X1 is X + 1. Also note that for is/2 to work, everything in the expression on the right must be previously instantiated. If any variables in the expression on the right do not have a value, you will get an instantiation error or, in the case of Turbo Prolog, Free variable in expression....
So you need to use different variables for expression results, and organize the code so that, if using is/2, variables in the expression are instantiated.
EDIT
I understand from Sergey Dymchenko that Turbo Prolog, unlike GNU or SWI, evaluates expressions for =/2. So the = will work in the given problem. However, the error regarding instantiation (or "free variable") is still caused by the same issue I mentioned above.
sum(N, N, N).
sum(M, N, S):-
N>M,
X is M+1,
sum(X, N, T),
S is M+T.
?- sum(1,5,N).
N = 15 .

Categorise List in Prolog

Alright so I am coding a parser for arithmetic equations. I get the input in a list, e.g. "10+20" = [49,48,43,50,48] and then I convert all the digits to there corresponding numbers e.g. [49,48,43,50,48] = [1,0,43,2,0] and from there I want to put integers > 10 back together.
Converting from ascii -> digits I use a maplist and number_codes to convert.
One approach I had was to just traverse the list and if it's 0-9 store it in a variable and then check the next number, 0-9 append it to the other variable and so on until I hit an operator. I can't seem to simply append digits as it were. Here's my current code.
expression(L) :-
maplist(chars, L, Ls).
chars(C, N) :-
(
C >= "0", "9" >= C -> number_codes(N, [C]);
N is C
).
Not sure if there's a simple way to add to my code (as far as I know, maplist only gives back a list of equal length to the list passed in but I could be mistaken).
Any help is appreciated :)
Yes, maplist only 'gives back' a list of equal length. Moreover, maplist applies a predicate only to one element (basically it's context-free). Therefore, it is not possible to do what you want (combine digits between operators to a single number) with maplist and you would have to write the recursion yourself.
However, you can do something way easier than all this converting back and forth:
expression(L, E):-
string_to_atom(L,A),
atom_to_term(A,E,[]).
Which works like this:
2 ?- expression("1+2",E).
E = 1+2.
3 ?- expression("1+2",E), X is E.
E = 1+2, X = 3.
4 ?- expression("1+2",E), X+Y = E.
E = 1+2, X = 1, Y = 2.
5 ?- expression("1+2+3",E), X+Y = E.
E = 1+2+3, X = 1+2, Y = 3.
Naturally, if you want a list with all the numbers involved you will have to do something recursive but this is kinda trivial imho.
If however you still want to do the converting, I suggest checking Definite Clause Grammars; it will simplify the task a lot.
I answered some time ago with an expression parser.
It will show you how to use DCG for practical tasks, and I hope you will appreciate the generality and simplicity of such approach.
Just a library predicate is required from SWI-Prolog, number//1, easily implemented in Sicstus. Let me know if you need more help on that.

Resources