99 questions/Solutions/93

Given a list of integer numbers, find a correct way of inserting arithmetic signs (operators) such that the result is a correct equation. Example: With the list of numbers [2,3,5,7,11] we can form the equations 2-3+5+7 = 11 or 2 = (3*5+7)/11 (and ten others!).

Division should be interpreted as operating on rationals, and division by zero should be avoided.

moduleP93whereimportControl.MonadimportData.ListimportData.MaybetypeEquation=(Expr,Expr)dataExpr=ConstInteger|BinaryExprOpExprderiving(Eq,Show)dataOp=Plus|Minus|Multiply|Dividederiving(Bounded,Eq,Enum,Show)typeValue=Rational-- top-level function: all correct equations generated from the list of-- numbers, as pretty strings.puzzle::[Integer]->[String]puzzlens=map(flipshowsEquation"")(equationsns)-- generate all correct equations from the list of numbersequations::[Integer]->[Equation]equations[]=error"empty list of numbers"equations[n]=error"only one number"equationsns=[(e1,e2)|(ns1,ns2)<-splitsns,(e1,v1)<-exprsns1,(e2,v2)<-exprsns2,v1==v2]-- generate all expressions from the numbers, except those containing-- a division by zero, or redundant right-associativity.exprs::[Integer]->[(Expr,Value)]exprs[n]=[(Constn,fromIntegern)]exprsns=[(Binarye1ope2,v)|(ns1,ns2)<-splitsns,(e1,v1)<-exprsns1,(e2,v2)<-exprsns2,op<-[minBound..maxBound],not(right_associativeope2),v<-maybeToList(applyopv1v2)]-- splittings of a list into two non-empty listssplits::[a]->[([a],[a])]splitsxs=tail(init(zip(initsxs)(tailsxs)))-- applying an operator to arguments may fail (division by zero)apply::Op->Value->Value->MaybeValueapplyPlusxy=Just(x+y)applyMinusxy=Just(x-y)applyMultiplyxy=Just(x*y)applyDividex0=NothingapplyDividexy=Just(x/y)-- e1 op (e2 op' e3) == (e1 op e2) op' e3right_associative::Op->Expr->Boolright_associativePlus(Binary_Plus_)=Trueright_associativePlus(Binary_Minus_)=Trueright_associativeMultiply(Binary_Multiply_)=Trueright_associativeMultiply(Binary_Divide_)=Trueright_associative__=False-- Printing of equations and expressionsshowsEquation::Equation->ShowSshowsEquation(l,r)=showsExprPrec0l.showString" = ".showsExprPrec0r-- all operations are left associativeshowsExprPrec::Int->Expr->ShowSshowsExprPrec_(Constn)=showsnshowsExprPrecp(Binarye1ope2)=showParen(p>op_prec)$showsExprPrecop_prece1.showString(opNameop).showsExprPrec(op_prec+1)e2whereop_prec=precedenceopprecedence::Op->IntprecedencePlus=6precedenceMinus=6precedenceMultiply=7precedenceDivide=7opName::Op->StringopNamePlus="+"opNameMinus="-"opNameMultiply="*"opNameDivide="/"

Unlike the Prolog solution, I've eliminated solutions like
"1+(2+3) = 6" as a trivial variant of "1+2+3 = 6" (cf the function right_associative).
Apart from that, the Prolog solution is shorter because it uses built-in evaluation and printing of expressions.