| 1 |
/** |
|---|
| 2 |
* String形式の数式を計算するクラス |
|---|
| 3 |
* |
|---|
| 4 |
* code is originally from |
|---|
| 5 |
* http://ericlin2.tripod.com/math2/EvaluateEqMxt.html |
|---|
| 6 |
* |
|---|
| 7 |
* 改造点 |
|---|
| 8 |
* ・ユーザー定義変数を使用可能にした。 |
|---|
| 9 |
* ・ユーザー定義関数も使用可能にした。 |
|---|
| 10 |
* |
|---|
| 11 |
* var num1 : Number = Calculator.evaluate("pow(2,4)"); |
|---|
| 12 |
* var num2 : Number = Calculator.evaluate("(x+y)*2", {x:5, y:3}); |
|---|
| 13 |
* var num3 : Number = Calculator.evaluate("f()*2", {f:function():void{return 5}}); |
|---|
| 14 |
*/ |
|---|
| 15 |
package |
|---|
| 16 |
{ |
|---|
| 17 |
public class Calculator |
|---|
| 18 |
{ |
|---|
| 19 |
public static const operatorSet : String = "+-*/^+-"; |
|---|
| 20 |
protected static var _argObj : Object |
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
/** |
|---|
| 24 |
* @param str 数式。Math関数はsqrt(a,b)といった形で使用可能。 |
|---|
| 25 |
* @param argObj ユーザー定義変数。ここで{x:1}等と定義することで、数式内で変数が使える |
|---|
| 26 |
*/ |
|---|
| 27 |
public static function evaluate(str : String, argObj:Object=null) : Number |
|---|
| 28 |
{ |
|---|
| 29 |
//ユーザー定義変数を一時保存 |
|---|
| 30 |
argObj = (argObj)? argObj : {}; |
|---|
| 31 |
|
|---|
| 32 |
str = str.split(" ").join(""); |
|---|
| 33 |
//trim off the space if any |
|---|
| 34 |
if (!str.length>0) { |
|---|
| 35 |
return 0; |
|---|
| 36 |
} |
|---|
| 37 |
// search for +,-,*,/,* , skip string in parenthesis and cut into two pieces accordingly |
|---|
| 38 |
// after this step , only function, parenthesis, variable or number are left |
|---|
| 39 |
var str2 : String = maskParenthesis(str); |
|---|
| 40 |
for (var k :int = 0; k<operatorSet.length; k++) { |
|---|
| 41 |
// becareful, we should check from right to left, not left to right |
|---|
| 42 |
var op : String = operatorSet.charAt(k); |
|---|
| 43 |
var start : Number= str.length-1; |
|---|
| 44 |
var i : int = 0; |
|---|
| 45 |
while ((i=str2.lastIndexOf(op, start))>=0) { |
|---|
| 46 |
if ((k<=1) && operatorSet.indexOf(str2.charAt(i-1))>=0) { |
|---|
| 47 |
//something like 3*-4; |
|---|
| 48 |
start = i-1; |
|---|
| 49 |
//################continue |
|---|
| 50 |
continue; |
|---|
| 51 |
} |
|---|
| 52 |
var lStr : String = str.substr(0, i); |
|---|
| 53 |
var rStr : String = str.substr(i+1, str.length-i-1); |
|---|
| 54 |
return handleOperator(op, lStr, rStr, argObj); |
|---|
| 55 |
} |
|---|
| 56 |
} |
|---|
| 57 |
// now we get only the parenthesis, function and pure number/variable here; now check function |
|---|
| 58 |
start = str.indexOf("("); |
|---|
| 59 |
if (start>=0) { |
|---|
| 60 |
var functionName : String = (str.substr(0, start)).toLowerCase(); |
|---|
| 61 |
var contentInParenthesis : String = str.substr(start+1, str.length-start-2); |
|---|
| 62 |
return handleFunction(functionName, contentInParenthesis, argObj); |
|---|
| 63 |
} |
|---|
| 64 |
// After those steps , only variable and pure number are left here, now check variable |
|---|
| 65 |
if (isNaN(Number(str))) { |
|---|
| 66 |
//ここでユーザー定義変数も使えるように改造 |
|---|
| 67 |
if( Math[str]!=undefined ){ |
|---|
| 68 |
return Math[str]; |
|---|
| 69 |
}else if(argObj[str]!=undefined && argObj[str] is Number){ |
|---|
| 70 |
return argObj[str]; |
|---|
| 71 |
} |
|---|
| 72 |
} |
|---|
| 73 |
// variable are screened off, here is pure value |
|---|
| 74 |
return Number(str); |
|---|
| 75 |
}; |
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
protected static function handleOperator(operator:String, lStr:String, rStr:String, argObj:Object):Number |
|---|
| 79 |
{ |
|---|
| 80 |
var num1 : Number = evaluate(lStr, argObj); |
|---|
| 81 |
var num2 : Number = evaluate(rStr, argObj); |
|---|
| 82 |
var val : Number |
|---|
| 83 |
switch (operator) { |
|---|
| 84 |
case "+" : |
|---|
| 85 |
val = num1+num2; |
|---|
| 86 |
break |
|---|
| 87 |
case "-" : |
|---|
| 88 |
val = num1-num2; |
|---|
| 89 |
break |
|---|
| 90 |
case "*" : |
|---|
| 91 |
val = num1*num2; |
|---|
| 92 |
break |
|---|
| 93 |
case "/" : |
|---|
| 94 |
val = num1/num2; |
|---|
| 95 |
break |
|---|
| 96 |
case "^" : |
|---|
| 97 |
val = Math.pow(num1, num2); |
|---|
| 98 |
break |
|---|
| 99 |
default : |
|---|
| 100 |
val = 0; |
|---|
| 101 |
} |
|---|
| 102 |
return val |
|---|
| 103 |
} |
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
/** |
|---|
| 107 |
* random 等引数0の呼び出しがあまりうまくいかなかったので色々改造 |
|---|
| 108 |
*/ |
|---|
| 109 |
protected static function handleFunction(functionName : String, contentInParenthesis:String, argObj:Object) : Number |
|---|
| 110 |
{ |
|---|
| 111 |
if (!functionName.length>0) { |
|---|
| 112 |
// pure parenthesis |
|---|
| 113 |
return evaluate(contentInParenthesis, argObj); |
|---|
| 114 |
} |
|---|
| 115 |
// It is a function, now check how many parameters |
|---|
| 116 |
var str2 :String = maskParenthesis(contentInParenthesis); |
|---|
| 117 |
var str : String = contentInParenthesis; |
|---|
| 118 |
var params : Array = []; |
|---|
| 119 |
|
|---|
| 120 |
if( str!="" && str2 !=""){ |
|---|
| 121 |
var i : * = -1; |
|---|
| 122 |
while (i=str2.indexOf(",")>=0) { |
|---|
| 123 |
var lStr : String = str.substr(0, i); |
|---|
| 124 |
str2 = str2.substr(i+1, str2.length-i-1); |
|---|
| 125 |
str = str.substr(i+1, str.length-i-1); |
|---|
| 126 |
params.push(evaluate(lStr, argObj)); |
|---|
| 127 |
} |
|---|
| 128 |
params.push(evaluate(str, argObj)); |
|---|
| 129 |
} |
|---|
| 130 |
|
|---|
| 131 |
var val:Number |
|---|
| 132 |
|
|---|
| 133 |
if(Math[functionName]!=undefined) |
|---|
| 134 |
{ |
|---|
| 135 |
val = Math[functionName].apply(null, params); |
|---|
| 136 |
}else if(argObj[functionName]!=undefined){ |
|---|
| 137 |
val = argObj[functionName].apply(null, params); |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
return val; |
|---|
| 141 |
} |
|---|
| 142 |
|
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 |
// ()内部をマスクする。 |
|---|
| 146 |
/** |
|---|
| 147 |
* 5+4*(4-3)+2 が 5+4*(((((+2 になる? |
|---|
| 148 |
* |
|---|
| 149 |
*/ |
|---|
| 150 |
protected static function maskParenthesis( str : String ) : String |
|---|
| 151 |
{ |
|---|
| 152 |
var parenthesisStack : Number = 0; |
|---|
| 153 |
var temp : String = ""; |
|---|
| 154 |
for( var i : int = 0; i < str.length; i++ ) |
|---|
| 155 |
{ |
|---|
| 156 |
var char :String = str.substr(i, 1); |
|---|
| 157 |
if( char == "(" ){ |
|---|
| 158 |
parenthesisStack++; |
|---|
| 159 |
} |
|---|
| 160 |
if( char == ")" ){ |
|---|
| 161 |
parenthesisStack--; |
|---|
| 162 |
} |
|---|
| 163 |
temp += (parenthesisStack == 0 ? char : "("); |
|---|
| 164 |
} |
|---|
| 165 |
if( parenthesisStack != 0 ){ |
|---|
| 166 |
// "(" と ")" の数が合わない |
|---|
| 167 |
throw new Error("The number of ( and ) does not match"); |
|---|
| 168 |
return ""; |
|---|
| 169 |
} |
|---|
| 170 |
return temp; |
|---|
| 171 |
} |
|---|
| 172 |
} |
|---|
| 173 |
} |
|---|