% Solution to ES 96 Homework 3 % Define safeIs operator. :-op(700,xfx,safeIs). % Define a handy predicate. :-op(1200,fx,once). once(X):-X,!. % Define the useful member procedure. member([H|_],H). member([_|T],H):-member(T,H). % % Determine type of operator. % % Comutative binary operators. Identity element returned. comBinOp(X,I):-member([o('+',0),o('*',1)],o(X,I)). % Binary operators. binOp(X):-member(['+','-','*','/'],Operator). % % safeIs clauses. % % Terminating condition: value is a number, atom, or unbound variable. safeIs(Value,Value):-( atomic(Value) ; var(Value) ). % Process an expression in which next operator is comutative. safeIs(Result,Expr):- Expr=..[Operator,_,_], comBinOp(Operator,Identity),!, flatten(Operator,Value,Symbolic,Expr), once ( Symbolic=[], Result=Value ; Value=Identity, Result=Symbolic ; Result=..[Operator,Symbolic,Value] ). % Process binary expressions. safeIs(Value,Expr):- Expr=..[Operator,Op1,Op2], binOp(Operator),!, safeIs(Val1,Op1), safeIs(Val2,Op2), Expr2 =.. [Operator,Val1,Val2], once ( number(Val1), number(Val2), Value is Expr2 ; Value = Expr2 ). % % Parse beginning part of expression consisting of consecutive occurence % of same operator, which must be of precedence yfx. % % flatten(Operator,Num,Es,Nested) % % When called, Nested is bound to an expression with Operator the % principle (next-executed) operator. On return, Num is bound to % the result of applying Operator to the numeric portion of Nested % and Es is bound to the non-numeric portion of Nested. % flatten(Operator,Num,Es,Nested):- Nested =.. [Operator,Op1,Op2],!, % Extract operands. V2 safeIs Op2, % Evaluate second operand. (Since % Operator is yfx, Operator cannot % be the next operation in Op2.) ( number(V2), % If V2 numeric, include it in NumExpr =.. [Operator,V1,V2], % expression for "is". E2=[] % No symbolic result of Op2. ; not(number(V2)), % If V2 not numeric, the numeric NumExpr = V1, % expression based only on V1. E2=V2 % Include V2 in symbolic expression. ), flatten(Operator,V1,E1,Op1), % Recursive call evaluating Op1. Num is NumExpr, % Evaluate numeric expression. ( E1=[],Es=E2 % If E1 not symbolic, return E2. ; E2=[],Es=E1 % If E2 not symbolic, return E1. ; not( ( E1=[] , E2=[] )), Es=..[Operator,E1,E2] % If E1 and E2 symbolic, combine. ). % If the clause below is executed, next operator in Expr is not Operator. flatten(Operator,Num,Es,Expr):- Val safeIs Expr, ( number(Val), % Return numeric value of Expr. Num=Val, Es=[] ; not(number(Val)), % Return symbolic value of Expr and comBinOp(Operator,Num), Es=Val % use Operator's identity for Num. ).