Skip to content

Instantly share code, notes, and snippets.

@sasame
Last active October 18, 2019 00:52
Show Gist options
  • Select an option

  • Save sasame/99db8cb20058d5d97ff15db492bf4b61 to your computer and use it in GitHub Desktop.

Select an option

Save sasame/99db8cb20058d5d97ff15db492bf4b61 to your computer and use it in GitHub Desktop.
文字列から四則演算などを計算する。
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();
}
}
@sasame

sasame commented Oct 18, 2019

Copy link
Copy Markdown
Author

// テストコード
{
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,PI
5)"));
Debug.Log(exp.Parse("sum(-1,2,-3,4,PI50.0)"));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment