For today's puzzler, I thought of a simple little thing that would make you work your string parsing part of the brain. I started out in Perl and absolutely love mucking around with strings, so I thought, why not share this love with everyone else? (Remind me to tell you about the contract work I did for Netscape back in 98 or so that was - basically - one big perl script to update their web site.)
This puzzle is rather simple. Build a form that will accept math questions in English. So for example:
one plus two
You only need to support numbers from zero to ten. You can report the answer as either a number or a word. You need to support "plus", "minus", "multiply", "divide". Bonus points if you catch a division by zero error. Bonus points if you ignore, and don't get tripped by, stuff at the end, like "is?"
As always, the prize is nothing, which is great since your tax burden on nothing will be roughly 33% of the value of nothing. You have 5 minutes to solve this, but if you spend more, I won't tell anyone. If you get fired doing this, I need someone to help dig up a mail post so I'd be happy to give you a new job.
Enjoy!


Comment 1 written by Teddy Payne on 5 May 2006, at 12:07 PM
<cffunction name="strEnglishToMath" returntype="numeric" output="true" hint="I return the math evlaution of an english expresss.">
<cfargument name="strExp" type="string" required="true">
<cfset numResult = 0>
<cfscript>
strExp = ReReplace(strExp,"zero","0","ALL");
strExp = ReReplace(strExp,"one","1","ALL");
strExp = ReReplace(strExp,"two","2","ALL");
strExp = ReReplace(strExp,"three","3","ALL");
strExp = ReReplace(strExp,"four","4","ALL");
strExp = ReReplace(strExp,"five","5","ALL");
strExp = ReReplace(strExp,"six","6","ALL");
strExp = ReReplace(strExp,"seven","7","ALL");
strExp = ReReplace(strExp,"eight","8","ALL");
strExp = ReReplace(strExp,"nine","9","ALL");
strExp = ReReplace(strExp,"ten","10","ALL");
strExp = ReReplace(strExp,"plus","+","ALL");
strExp = ReReplace(strExp,"minus","-","ALL");
strExp = ReReplace(strExp,"multiply","*","ALL");
strExp = ReReplace(strExp,"divide","/","ALL");
reScript = "[0-9]+[ ]+[\+\-\/\*]+[ ]+[0-9]+";
strPos = REFind(reScript, strExp,1,"True");
strExp = mid(strExp, strPos.pos[1], strPos.len[1]);
leftNumber = Trim(listgetat(strExp,1,"+-/*"));
rightNumber = Trim(listgetat(strExp,2,"+-/*"));
reScript = "[\+\-\/\*]";
strPos = REFind(reScript, strExp,1,"True");
operator = mid(strExp, strPos.pos[1], strPos.len[1]);
if(not (rightNumber eq 0 and operator eq "/"))
{
switch(operator)
{
case "+":
{
numResult = leftNumber + rightNumber;
break;
}
case "-":
{
numResult = leftNumber - rightNumber;
break;
}
case "/":
{
numResult = leftNumber / rightNumber;
break;
}
case "*":
{
numResult = leftNumber * rightNumber;
break;
}
}
}
</cfscript>
<cfreturn numResult>
</cffunction>
<cfset strExp = "five divide zero is?">
<cfoutput>
#strEnglishToMath(strExp)#
</cfoutput>
Comment 2 written by Kyle Hayes on 5 May 2006, at 12:18 PM
function NaturalToNum(str) {
switch (str) {
case "one":
returnVal = 1;
break;
case "two":
returnVal = 2;
break;
case "three":
returnVal = 3;
break;
case "four":
returnVal = 4;
break;
case "five":
returnVal = 5;
break;
case "six":
returnVal = 6;
break;
case "seven":
returnVal = 7;
break;
case "eight":
returnVal = 8;
break;
case "nine":
returnVal = 9;
break;
case "ten":
returnVal = 10;
break;
default:
returnVal = str;
break;
}
return returnVal;
}
function NaturalToSign(str) {
switch (str) {
case "plus":
returnVal = "+";
break;
case "times":
returnVal = "*";
break;
case "minus":
returnVal = "-";
break;
case "divided":
returnVal = "/";
break;
default:
returnVal = str;
break;
}
return returnVal;
}
function EvalNaturalEquation(str) {
strArr = ListToArray(str," ");
for(i = 1; i lte ArrayLen(strArr); i = i + 1) {
if(NaturalToNum(NaturalToSign(strArr[i])) eq strArr[i]) {
ArrayDeleteAt(strArr,i);
}
else {
strArr[i] = NaturalToNum(NaturalToSign(strArr[i]));
}
}
strList = ArrayToList(strArr, " ");
return Evaluate(strList);
}
</cfscript>
<cfif isDefined('form.equation') and len(trim(form.equation))>
<cfset answer = EvalNaturalEquation(form.equation)>
</cfif>
Comment 3 written by Kyle Hayes on 5 May 2006, at 12:19 PM
http://www.kylehayes.info/quizzes/naturalCalc.cfm
Comment 4 written by Kyle Hayes on 5 May 2006, at 12:19 PM
Comment 5 written by Kyle Hayes on 5 May 2006, at 12:23 PM
try {
returnVal = Evaluate(strList);
}
catch(any e){
returnVal = "Cannot divide by 0";
}
return returnVal;
Comment 6 written by Tuyen on 5 May 2006, at 12:44 PM
Tried your demo: Entered "onex plus twoy", got the wrong error message: "Cannot divide by 0"...
Happy Friday!
Comment 7 written by David on 5 May 2006, at 1:39 PM
Couldn't find any replacement for using Evaluate(), took too long trying to come up with one...
<!--- change this parameter to test --->
<cfparam name="form.mathstring" default="fivex times one" />
<cfscript>
//set invalid variables to 0 : used to check for division against zero invalid =1 means dividing by zero : invalid 3 means invalid characters
variables.invalid = 0;
variables.number1=0;
variables.number2=0;
//set 2D array to put in number calculation **** 1-1 = operator 1-2 = first number 1-3 = second number
variables.mathOperators = ArrayNew(2);
//set array for string operators
variables.MathOperatorsArray = ArrayNew(1);
//set array for symbol operators
variables.MathSymbolArray = ArrayNew(1);
//set array for string number = Array number
variables.NumberArray = Arraynew(1);
variables.mathOperatorsArray[1] = 'plus';
variables.mathOperatorsArray[2] = 'minus';
variables.mathOperatorsArray[3] = 'multiply';
variables.mathOperatorsArray[4]= 'divide';
variables.mathOperatorsArray[5]='times';
variables.mathSymbolArray[1] = '+';
variables.mathSymbolArray[2] = '-';
variables.mathSymbolArray[3] = '*';
variables.mathSymbolArray[4] = '/';
variables.mathSymbolArray[5] = '*';
variables.numberArray[1] = 'one';
variables.numberArray[2] = 'two';
variables.numberArray[3] = 'three';
variables.numberArray[4] = 'four';
variables.numberArray[5] = 'five';
variables.numberArray[6] = 'six';
variables.numberArray[7] = 'seven';
variables.numberArray[8] = 'eight';
variables.numberArray[9] = 'nine';
variables.numberArray[10] = 'ten';
variables.numberArray[11] = 'zero';
//check loop for operators
for(i=1;i lte ArrayLen(variables.MathOperatorsArray); i=i+1)
{
//check to see which operator to use
if (form.mathstring contains variables.MathOperatorsArray[i])
{
// set math operator into array
variables.mathOperators[i][1]=variables.mathSymbolArray[i];
//get first number
variables.firstnumber = Gettoken(form.mathstring,1,' ');
//get second number
variables.secondnumber = GetToken(form.mathstring,3,' ');
//find numerical value of strings
for (num1 = 1;num1 lte ArrayLen(variables.numberArray); num1 = num1 + 1)
{
if(comparenocase(variables.numberArray[num1],variables.firstnumber) eq 0) variables.number1 =num1;
if(comparenocase(variables.numberArray[num1],variables.secondnumber) eq 0)variables.number2 =num1;
}
//set numbers into array
variables.mathOperators[i][2] = variables.number1;
variables.mathOperators[i][3] = variables.number2;
//check for invalid characters
if(variables.number1 eq 0 or variables.number2 eq 0)variables.invalid = 3;
// check for zero divisability ********************
if(variables.secondnumber eq 11) variables.invalid = 1;
}
}
//check to see if we should show division by zero
if(variables.invalid eq 0){
//Do math and check for divisible by 0
//loop through mathoperators array to find what to calculate
for(math=1; math lte ArrayLen(variables.mathoperators); math = math +1)
{
if(not ArrayIsEmpty(variables.mathoperators[math]))
variables.calcoutput = 'Your answer is: ' & Evaluate('#variables.mathoperators[math][2]# #variables.mathoperators[math][1]# #variables.mathoperators[math][3]#');
}
}
// say something to tell them that their total was division by 0
else
{
if (variables.invalid eq 1)
variables.calcoutput = 'You know better than to Divide by zero punk!'; else variables.calcoutput = 'Invalid math string! Cannot process the form query.';
}
writeoutput(variables.calcoutput);
</cfscript>
<CFDUMP var="#variables.mathoperators#" />
Comment 8 written by Daniel Daugherty on 5 May 2006, at 1:53 PM
http://www.google.com/help/features.html#calculato...
I am a big fan of using existing code when possible.
Comment 9 written by Raymond Camden on 5 May 2006, at 1:59 PM
Comment 10 written by Chris Mallinson on 5 May 2006, at 2:03 PM
I'm pretty sure it works...
<cfif isdefined("form.submit")>
<cfscript>
mathString = form.question;
mathString = rereplacenocase(mathString,'(divided by)|(by)|(over)|(into)','/','all');
mathString = rereplacenocase(mathString,'(plus)|(added to)|(and)|(to)','+','all');
mathString = rereplacenocase(mathString,'(minus)|(less)|(subtract)','-','all');
mathString = rereplacenocase(mathString,'(from\s)','- -','all');
mathString = rereplacenocase(mathString,'(multiplied by)|(times)','*','all');
mathString = rereplacenocase(trim(mathString),'^multiply\s([a-z0-9]*)\s\+','\1 *');
mathString = rereplacenocase(trim(mathString),'^((add)|(subtract)|(divide)|(multiply))','');
numberStrings = 'zero,one,two,three,four,five,six,seven,eight,nine,ten';
numberDigits = '0,1,2,3,4,5,6,7,8,9,10';
mathString = trim(replacelist(mathString,numberStrings,numberDigits));
if (refind('/ 0$',mathString))
answer = 'undefined';
else
answer = evaluate(mathString);
writeOutput(answer);
</cfscript>
</cfif>
<cfform name="math" action="#CGI.SCRIPT_NAME#" method="post" preservedata="yes">
<cfinput name="question" type="text" size="30">
<input type="submit" name="submit" value="do the math" />
</cfform>
Comment 11 written by Kyle Hayes on 5 May 2006, at 2:24 PM
http://www.google.com/search?q=five+plus+two&b...
Comment 12 written by Kyle Hayes on 5 May 2006, at 2:25 PM
http://www.google.com/search?hl=en&sa=X&oi...
[Add Comment] [Subscribe to Comments]