Last active
October 18, 2019 00:52
-
-
Save sasame/99db8cb20058d5d97ff15db492bf4b61 to your computer and use it in GitHub Desktop.
文字列から四則演算などを計算する。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| public class DExpression | |
| { | |
| public delegate object Function(Node n); | |
| static char[] _operators = new char[] { '+', '-', '*', '/' }; | |
| static char[] _bracket = new char[] { '(', ')' }; | |
| Dictionary<string, object> _constants = new Dictionary<string, object>(); | |
| Dictionary<string, Function> _functions = new Dictionary<string, Function>(); | |
| // 定数追加 | |
| public void AddConstant(string id,object value) | |
| { | |
| if ((value is long) || (value is double)) | |
| { | |
| _constants.Add(id, value); | |
| return; | |
| } | |
| if (value is int) | |
| { | |
| _constants.Add(id, (long)(int)value); | |
| return; | |
| } | |
| throw new Exception("Invalid type constant:" + id + ":" + value.GetType().Name); | |
| } | |
| public void AddFunction(string id,Function f) | |
| { | |
| _functions.Add(id, f); | |
| } | |
| public class Node | |
| { | |
| object _value; | |
| string _operator = null; | |
| string _functionName = null; | |
| Function _function = null; | |
| List<Node> _children = new List<Node>(); | |
| Node _parent = null; | |
| public override string ToString() | |
| { | |
| if (_operator != null) | |
| { | |
| return _operator; | |
| } | |
| if (_value != null) | |
| { | |
| return _value.ToString(); | |
| } | |
| if (_function!=null) | |
| { | |
| string s = _functionName + "("; | |
| foreach (var v in _children) | |
| { | |
| s += v.ToString(); | |
| } | |
| return s + ")"; | |
| } | |
| if (_children.Count > 0) | |
| { | |
| string s = "("; | |
| foreach (var v in _children) | |
| { | |
| s += v.ToString(); | |
| } | |
| return s + ")"; | |
| } | |
| throw new Exception("err ToString"); | |
| } | |
| // creators | |
| public static Node CreateValue(object val) | |
| { | |
| Node n = new Node(); | |
| n._value = val; | |
| return n; | |
| } | |
| public static Node CreateOperator(string op) | |
| { | |
| Node n = new Node(); | |
| n._operator = op; | |
| return n; | |
| } | |
| public static Node CreateBracket(Node parent) | |
| { | |
| Node n = new Node(); | |
| n._parent = parent; | |
| return n; | |
| } | |
| public static Node CreateFunction(string funcName,Function f) | |
| { | |
| Node n = new Node(); | |
| n._functionName = funcName; | |
| n._function = f; | |
| return n; | |
| } | |
| public static Node CreateFunctionArgument(Node parent) | |
| { | |
| Node n = new Node(); | |
| n._parent = parent; | |
| return n; | |
| } | |
| public static Node RangeBracket(Node n, int start, int end) | |
| { | |
| Node b = CreateBracket(n); | |
| int num = end - start + 1; | |
| for (int i = 0; i < num; ++i) | |
| { | |
| var c = n._children[start]; | |
| n._children.RemoveAt(start); | |
| b.Add(c); | |
| } | |
| n.Insert(b, start); | |
| return b; | |
| } | |
| // properties | |
| public Node Parent | |
| { | |
| get { return _parent; } | |
| } | |
| public List<Node> Children | |
| { | |
| get { return _children; } | |
| } | |
| public object Value | |
| { | |
| get | |
| { | |
| if (_value != null) | |
| { | |
| return _value; | |
| } | |
| return Calculate(); | |
| } | |
| } | |
| public Function Function | |
| { | |
| get { return _function; } | |
| } | |
| public double CastDouble() | |
| { | |
| object val = Value; | |
| if (val != null) | |
| { | |
| if (val is double) | |
| { | |
| return (double)val; | |
| }else if (val is long) | |
| { | |
| return (long)val; | |
| } | |
| } | |
| throw new Exception("Invalid Type"); | |
| } | |
| void setMinus() | |
| { | |
| if (_value != null) | |
| { | |
| if (_value is double) | |
| { | |
| _value = -(double)_value; | |
| return; | |
| } | |
| else if (_value is long) | |
| { | |
| _value = -(long)_value; | |
| return; | |
| } | |
| } | |
| throw new Exception("Invalid Type"); | |
| } | |
| // method | |
| public void Add(Node c) | |
| { | |
| _children.Add(c); | |
| c._parent = this; | |
| } | |
| public void Insert(Node c,int index) | |
| { | |
| _children.Insert(index, c); | |
| c._parent = this; | |
| } | |
| public void SolveMinus(string op) | |
| { | |
| if (_children.Count>=2) | |
| { | |
| var first = _children[0]; | |
| if (first._operator == op) | |
| { | |
| var second = _children[1]; | |
| if (second._value!=null) | |
| { | |
| second.setMinus(); | |
| _children.RemoveAt(0); | |
| } | |
| } | |
| } | |
| foreach (var c in _children) | |
| { | |
| c.SolveMinus(op); | |
| } | |
| } | |
| public void SolveOrder(string op) | |
| { | |
| while(_children.Count >= 5) | |
| { | |
| bool proc = false; | |
| for(int i=1;i<_children.Count-1;++i) | |
| { | |
| var cur = _children[i]; | |
| if (cur._operator==op.ToString()) | |
| { | |
| Node.RangeBracket(this, i - 1, i + 1); | |
| proc = true; | |
| break; | |
| } | |
| } | |
| if (proc==false) break; | |
| } | |
| foreach(var c in _children) | |
| { | |
| c.SolveOrder(op); | |
| } | |
| } | |
| // 計算 | |
| public object Calculate() | |
| { | |
| var first = _children[0]; | |
| if (_function!=null) | |
| { | |
| return _function(this); | |
| } | |
| Node op = null; | |
| object val = first.Value; | |
| for(int i=1;i< _children.Count;++i) | |
| { | |
| var n = _children[i]; | |
| if (n._operator != null) | |
| { | |
| op = n; | |
| } | |
| else | |
| { | |
| switch(op._operator) | |
| { | |
| case "+": | |
| val = opAdd(val, n.Value); | |
| break; | |
| case "-": | |
| val = opSub(val, n.Value); | |
| break; | |
| case "*": | |
| val = opMul(val, n.Value); | |
| break; | |
| case "/": | |
| val = opDiv(val, n.Value); | |
| break; | |
| } | |
| } | |
| } | |
| return val; | |
| } | |
| // + | |
| object opAdd(object a, object b) | |
| { | |
| if (a is long) | |
| { | |
| if (b is long){return ((long)a) + ((long)b);} | |
| else if (b is double){return ((long)a) + (double)b;} | |
| } | |
| else | |
| { | |
| double dA = (double)a; | |
| if (b is long){return dA + ((long)b);} | |
| else if (b is double){return dA + (double)b;} | |
| } | |
| throw new Exception("err"); | |
| } | |
| // - | |
| object opSub(object a, object b) | |
| { | |
| if (a is long) | |
| { | |
| if (b is long){return ((long)a) - ((long)b);} | |
| else if (b is double){return ((long)a) - (double)b;} | |
| } | |
| else | |
| { | |
| double dA = (double)a; | |
| if (b is long){return dA - ((long)b);} | |
| else if (b is double){return dA - (double)b;} | |
| } | |
| throw new Exception("err"); | |
| } | |
| // * | |
| object opMul(object a, object b) | |
| { | |
| if (a is long) | |
| { | |
| if (b is long){return ((long)a) * ((long)b);} | |
| else if (b is double){return ((long)a) * (double)b;} | |
| } | |
| else | |
| { | |
| double dA = (double)a; | |
| if (b is long){return dA * ((long)b);} | |
| else if (b is double){return dA * (double)b;} | |
| } | |
| throw new Exception("err"); | |
| } | |
| // / | |
| object opDiv(object a, object b) | |
| { | |
| if (a is long) | |
| { | |
| if (b is long){return ((long)a) / ((long)b);} | |
| else if (b is double){return ((long)a) / (double)b;} | |
| } | |
| else | |
| { | |
| double dA = (double)a; | |
| if (b is long){return dA / ((long)b);} | |
| else if (b is double){return dA / (double)b;} | |
| } | |
| throw new Exception("err"); | |
| } | |
| } | |
| static bool isNonNumber(char c) | |
| { | |
| foreach (var t in _operators) | |
| { | |
| if (t == c) | |
| { | |
| return true; | |
| } | |
| } | |
| if (isBracket(c)) | |
| { | |
| return true; | |
| } | |
| if (c == ',') | |
| { | |
| return true; | |
| } | |
| return false; | |
| } | |
| static bool isBracket(char c) | |
| { | |
| foreach (var t in _bracket) | |
| { | |
| if (t == c) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| static bool isOperator(char c) | |
| { | |
| foreach (var t in _operators) | |
| { | |
| if (t == c) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| public object Parse(string s) | |
| { | |
| int len = s.Length; | |
| Node root = new Node(); | |
| Node currentNode = root; | |
| for (int i=0;i<len;++i) | |
| { | |
| char cur = s[i]; | |
| if (char.IsWhiteSpace(cur)) | |
| { | |
| continue; | |
| } | |
| switch(cur) | |
| { | |
| case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': | |
| { | |
| // 数値 | |
| int start = i; | |
| bool hasPoint = false; | |
| for (; i < len; ++i) | |
| { | |
| char c = s[i]; | |
| if (c == '.') | |
| { | |
| hasPoint = true; | |
| }else if (isNonNumber(c)) | |
| { | |
| break; | |
| } | |
| } | |
| string numStr = s.Substring(start, i - start); | |
| object val = null; | |
| if (hasPoint) | |
| { | |
| double d; | |
| if (double.TryParse(numStr, out d)) | |
| { | |
| val = d; | |
| } | |
| } | |
| else | |
| { | |
| long v; | |
| if (long.TryParse(numStr, out v)) | |
| { | |
| val = v; | |
| } | |
| } | |
| currentNode.Add(Node.CreateValue(val)); | |
| --i; | |
| } | |
| break; | |
| case '+': case '-': case '*': case '/': | |
| { | |
| var n = Node.CreateOperator(cur.ToString()); | |
| currentNode.Add(n); | |
| } | |
| break; | |
| case '(': | |
| { | |
| // 一つ前がファンクションだった場合 | |
| if ((currentNode.Children.Count > 0) | |
| && currentNode.Children[currentNode.Children.Count - 1].Function != null) | |
| { | |
| var func = currentNode.Children[currentNode.Children.Count - 1]; | |
| currentNode = Node.CreateFunctionArgument(func); | |
| func.Add(currentNode); | |
| } | |
| else | |
| { | |
| var n = Node.CreateBracket(currentNode); | |
| currentNode.Add(n); | |
| currentNode = n; | |
| } | |
| } | |
| break; | |
| case ')': | |
| { | |
| currentNode = currentNode.Parent; | |
| // もう一つ親がファンクションならもう一つ戻る | |
| if (currentNode.Function!=null) | |
| { | |
| currentNode = currentNode.Parent; | |
| } | |
| } | |
| break; | |
| case ',': | |
| { | |
| var parentFunc = currentNode.Parent; | |
| if ((parentFunc != null) && (parentFunc.Function!=null)) | |
| { | |
| var arg = Node.CreateFunctionArgument(parentFunc); | |
| parentFunc.Add(arg); | |
| currentNode = arg; | |
| } | |
| else | |
| { | |
| throw new Exception("Invalid camma."); | |
| } | |
| } | |
| break; | |
| default: | |
| { | |
| // 数値 | |
| int start = i; | |
| for (; i < len; ++i) | |
| { | |
| char c = s[i]; | |
| if (char.IsWhiteSpace(c)) | |
| { | |
| break; | |
| }else if (isBracket(c) || isOperator(c)) | |
| { | |
| break; | |
| } | |
| } | |
| string idStr = s.Substring(start, i - start); | |
| if (_constants.ContainsKey(idStr)) | |
| { | |
| currentNode.Add(Node.CreateValue(_constants[idStr])); | |
| }else if (_functions.ContainsKey(idStr)) | |
| { | |
| currentNode.Add(Node.CreateFunction(idStr,_functions[idStr])); | |
| } | |
| --i; | |
| } | |
| break; | |
| } | |
| } | |
| if (currentNode != root) | |
| { | |
| throw new Exception("err"); | |
| } | |
| // 数値前のマイナス記号の解決 | |
| root.SolveMinus("-"); | |
| // 計算順番の解決 | |
| for(int i=0;i<_operators.Length-1;++i) | |
| { | |
| string op = _operators[_operators.Length - i -1].ToString(); | |
| root.SolveOrder(op); | |
| } | |
| return root.Calculate(); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
// テストコード
{
DExpression exp = new DExpression();
exp.AddConstant("PI", 3.1415);
exp.AddConstant("A", 16);
exp.AddConstant("B", 24);
exp.AddFunction("sin", (n) => {
if (n.Children.Count == 1)
{
return Math.Sin(n.Children[0].CastDouble());
}
throw new Exception("invalid argument count");
});
exp.AddFunction("sum", (n) => {
if (n.Children.Count>0)
{
double sum = 0.0;
foreach(var c in n.Children)
{
sum += c.CastDouble();
}
return sum;
}
throw new Exception("invalid argument count");
});
Debug.Log(exp.Parse("1+123"));
Debug.Log(exp.Parse("3.0+5.5"));
Debug.Log(exp.Parse("7+3.14"));
Debug.Log(exp.Parse("7.12+88"));
Debug.Log(exp.Parse("1-5 + 3.13"));
Debug.Log(exp.Parse("100/10"));
Debug.Log(exp.Parse("10010"));
Debug.Log(exp.Parse("(3 * 5) + 11"));
Debug.Log(exp.Parse("3 * 5 + (4/2)"));
Debug.Log(exp.Parse("4 * 6 + 8 * 7"));
Debug.Log(exp.Parse("4.0 / 6 + 3 / 1.5"));
Debug.Log(exp.Parse("PI * 2.0"));
Debug.Log(exp.Parse("A + B"));
Debug.Log(exp.Parse("A * B"));
Debug.Log(exp.Parse("sin(PI)"));
Debug.Log(exp.Parse("sin(PI/2)"));
Debug.Log(exp.Parse("sum(1,2,3,4,PI5)"));
Debug.Log(exp.Parse("sum(-1,2,-3,4,PI50.0)"));
}