Computer and man - how difficult it is for us to understand each other. In essence, the programming process is an explanation to the machine that you want from it in the language it understands.
/// <summary> </summary> /// <param name="StrExpression"> </param> /// <returns> </returns> [NotNull] public MathExpression Parse([NotNull] string StrExpression) { Contract.Requires(!string.IsNullOrWhiteSpace(StrExpression)); Contract.Ensures(Contract.Result<MathExpression>() != null); StrPreprocessing(ref StrExpression); OnStringPreprocessing(ref StrExpression); var expression = new MathExpression(StrExpression, this); ProcessVariables(expression); ProcessFunctions(expression); return expression; }
/// <summary> </summary> /// <param name="Str"> </param> // , protected virtual void StrPreprocessing([NotNull] ref string Str) { Contract.Requires(!string.IsNullOrEmpty(Str)); Contract.Ensures(!string.IsNullOrEmpty(Contract.ValueAtReturn(out Str))); Str = new string(Str.Where(f_ExcludeCharsSet.NotContains).ToArray()); }
/// <summary> </summary> public event EventHandler<EventArgs<string>> StringPreprocessing; /// <summary> </summary> /// <param name="args"> , </param> protected virtual void OnStringPreprocessing([NotNull] EventArgs<string> args) { Contract.Requires(args != null); Contract.Requires(args.Argument != null); Contract.Requires(args.Argument != string.Empty); Contract.Ensures(args.Argument != null); Contract.Ensures(args.Argument != string.Empty); StringPreprocessing?.Invoke(this, args); } /// <summary> </summary> /// <param name="StrExpression"> </param> private void OnStringPreprocessing([NotNull] ref string StrExpression) { Contract.Requires(!string.IsNullOrEmpty(StrExpression)); Contract.Ensures(Contract.ValueAtReturn(out StrExpression) != null); Contract.Ensures(Contract.ValueAtReturn(out StrExpression) != string.Empty); var args = new EventArgs<string>(StrExpression); OnStringPreprocessing(args); StrExpression = args.Argument; }
/// <summary> </summary> /// <param name="StrExpression"> </param> /// <param name="Parser"> </param> internal MathExpression([NotNull] string StrExpression, [NotNull] ExpressionParser Parser) : this() { Contract.Requires(!string.IsNullOrEmpty(StrExpression)); Contract.Requires(Parser != null); Contract.Ensures(Tree != null); var terms = new BlockTerm(StrExpression); // var root = terms.GetSubTree(Parser, this); // f_ExpressionTree = new ExpressionTree(root); // }
/// <summary> </summary> /// <param name="Str"> </param> public BlockTerm(string Str) : this("", Str, "") { } /// <summary> </summary> /// <param name="OpenBracket"> </param> /// <param name="Str"> </param> /// <param name="CloseBracket"> </param> public BlockTerm([NotNull] string OpenBracket, [NotNull] string Str, [NotNull] string CloseBracket) : base(string.Format("{0}{2}{1}", OpenBracket ?? "", CloseBracket ?? "", Str)) { Contract.Requires(!string.IsNullOrEmpty(Str)); f_OpenBracket = OpenBracket; f_CloseBracket = CloseBracket; f_Terms = GetTerms(Str); }
/// <summary> </summary> abstract class Term { /// <summary> </summary> protected string f_Value; /// <summary> </summary> /// <param name="Value"> </param> protected Term(string Value) { f_Value = Value; } /// <summary> </summary> /// <param name="Parser"> </param> /// <param name="Expression"> </param> /// <returns> ., </returns> [NotNull] public abstract ExpressionTreeNode GetSubTree([NotNull] ExpressionParser Parser, [NotNull] MathExpression Expression); /// <summary> .</summary> /// <returns> .</returns> public override string ToString() => f_Value; }
/// <summary> </summary> /// <param name="Str"> </param> /// <returns> </returns> [CanBeNull] private static Term[] GetTerms([CanBeNull] string Str) { if(Str == null) return null; if(Str.Length == 0) return new Term[0]; var pos = 0; var len = Str.Length; var result = new List<Term>(); while(pos < len) { var c = Str[pos]; if(char.IsLetter(c) || c == '∫') { Term value = new StringTerm(GetNameString(Str, ref pos)); if(pos < len) switch(Str[pos]) { case '(': { var blokStr = Str.GetBracketText(ref pos); var block = new BlockTerm("(", blokStr, ")"); value = new FunctionTerm((StringTerm)value, block); } break; case '[': { var blokStr = Str.GetBracketText(ref pos, "[", "]"); var block = new BlockTerm("[", blokStr, "]"); value = new FunctionTerm((StringTerm)value, block); } break; case '{': { var blokStr = Str.GetBracketText(ref pos, "{", "}"); var block = new BlockTerm("{", blokStr, "}"); value = new FunctionTerm((StringTerm)value, block); } break; } if(pos < len && Str[pos] == '{') value = new FunctionalTerm ( (FunctionTerm)value, new BlockTerm("{", Str.GetBracketText(ref pos, "{", "}"), "}") ); result.Add(value); } else if(char.IsDigit(c)) result.Add(new NumberTerm(GetNumberString(Str, ref pos))); else switch(c) { case '(': { var blokStr = Str.GetBracketText(ref pos); var block = new BlockTerm("(", blokStr, ")"); result.Add(block); } break; case '[': { var blokStr = Str.GetBracketText(ref pos, "[", "]"); var block = new BlockTerm("[", blokStr, "]"); result.Add(block); } break; case '{': { var blokStr = Str.GetBracketText(ref pos, "{", "}"); var block = new BlockTerm("{", blokStr, "}"); result.Add(block); } break; default: result.Add(new CharTerm(Str[pos++])); break; } } return result.ToArray(); }
/// <summary> </summary> /// <param name="Str"> </param> /// <param name="pos"> </param> /// <returns> </returns> private static string GetNameString([NotNull] string Str, ref int pos) { Contract.Requires(!string.IsNullOrEmpty(Str)); Contract.Ensures(Contract.ValueAtReturn(out pos) >= 0); Contract.Ensures(Contract.ValueAtReturn(out pos) < Str.Length); var result = ""; var L = Str.Length; var i = pos; while(i < L && (char.IsLetter(Str[i]) || Str[i] == '∫')) result += Str[i++]; if(i == L || !char.IsDigit(Str[i])) { pos = i; return result; } while(i < L && char.IsDigit(Str[i])) result += Str[i++]; pos += result.Length; return result; }
/// <summary> /// , /// </summary> /// <param name="Str"> </param> /// <param name="Offset"> /// - /// </param> /// <param name="Open"> </param> /// <param name="Close"> </param> /// <returns>, </returns> /// <exception cref="FormatException"> /// , /// /// </exception> public static string GetBracketText(this string Str, ref int Offset, string Open = "(", string Close = ")") { var Start = Str.IndexOf(Open, Offset, StringComparison.Ordinal); if(Start == -1) return null; var Stop = Str.IndexOf(Close, Start + 1, StringComparison.Ordinal); if(Stop == -1) throw new FormatException(); var start = Start; do { start = Str.IndexOf(Open, start + 1, StringComparison.Ordinal); if(start != -1 && start < Stop) Stop = Str.IndexOf(Close, Stop + 1, StringComparison.Ordinal); } while(start != -1 && start < Stop); if(Stop == -1 || Stop < Start) throw new FormatException(); Offset = Stop + Close.Length; Start += Open.Length; return Str.Substring(Start, Stop - Start); }
/// <summary> </summary> /// <param name="Str"> </param> /// <param name="pos"> </param> /// <returns> </returns> private static string GetNumberString([NotNull] string Str, ref int pos) { Contract.Requires(!string.IsNullOrEmpty(Str)); Contract.Ensures(Contract.ValueAtReturn(out pos) >= 0); Contract.Ensures(Contract.ValueAtReturn(out pos) < Str.Length); var p = pos; var l = Str.Length; while(p < l && !char.IsDigit(Str, p)) p++; if(p >= l) return null; var start = p; while(p < l && char.IsDigit(Str, p)) p++; pos = p; return Str.Substring(start, p - start); }
/// <summary> </summary> /// <param name="Parser"> </param> /// <param name="Expression"> </param> /// <returns> </returns> public override ExpressionTreeNode GetSubTree(ExpressionParser Parser, MathExpression Expression) { Contract.Requires(Parser != null); Contract.Requires(Expression != null); Contract.Ensures(Contract.Result<ExpressionTreeNode>() != null); var separator = Parser.ExpressionSeparator; // - // , - // var roots = Terms .Split(t => t is CharTerm && ((CharTerm)t).Value == separator) .Select(g => Parser.GetRoot(g, Expression)).ToArray(); if(roots.Length == 1) return roots[0]; // , // ExpressionTreeNode argument = null; // for(var i = 0; i < roots.Length; i++) // { var root = roots[i]; ExpressionTreeNode arg; // if(root is FunctionArgumentNode) // - arg = root; // -- else if(root is FunctionArgumentNameNode) // - // -- arg = new FunctionArgumentNode(root as FunctionArgumentNameNode); else if(root is VariantOperatorNode && root.Left is VariableValueNode) arg = new FunctionArgumentNode(((VariableValueNode)root.Left).Name, root.Right); else // - arg = new FunctionArgumentNode("", root); // -- if(argument == null) argument = arg; // , , else // argument = argument.Right = arg; // } // , - - if(argument == null) throw new FormatException(" "); return argument.Root; // }
/// <summary> </summary> /// <typeparam name="T"> </typeparam> /// <param name="array"> </param> /// <param name="Splitter">, , </param> /// <returns> /// , . /// . /// </returns> [NotNull] public static T[][] Split<T>([NotNull] this T[] array, [NotNull] Func<T, bool> Splitter) { Contract.Requires(array != null); Contract.Requires(Splitter != null); Contract.Ensures(Contract.Result<T[][]>() != null); var result = new List<T[]>(array.Length); var aggregator = new List<T>(array.Length); for(var i = 0; i < array.Length; i++) { var value = array[i]; if(Splitter(value) && aggregator.Count != 0) { result.Add(aggregator.ToArray()); aggregator.Clear(); } else aggregator.Add(value); } if(aggregator.Count != 0) result.Add(aggregator.ToArray()); return result.ToArray(); }
/// <summary> </summary> /// <param name="Group"> </param> /// <param name="MathExpression"> </param> /// <returns> .</returns> internal ExpressionTreeNode GetRoot([NotNull] Term[] Group, [NotNull] MathExpression MathExpression) { Contract.Requires(Group != null); Contract.Requires(MathExpression != null); Contract.Ensures(Contract.Result<ExpressionTreeNode>() != null); // ExpressionTreeNode Last = null; for(var i = 0; i < Group.Length; i++) // { var node = Group[i].GetSubTree(this, MathExpression); // // ... if(Group[i] is NumberTerm) // ... , { //... if(i + 2 < Group.Length && NumberTerm.TryAddFractionPart(ref node, Group[i + 1], DecimalSeparator, Group[i + 2])) i += 2; //... . } else if(Group[i] is BlockTerm) //... ( ) node = new ComputedBracketNode( // - new Bracket( // : (((BlockTerm)Group[i]).OpenBracket), // ((BlockTerm)Group[i]).CloseBracket), // node); // // Combine(Last, Last = node); // if(Last.IsRoot && Last is VariantOperatorNode && Last.Left is VariableValueNode) Last = new FunctionArgumentNameNode(((VariableValueNode)Last.Left).Name); OnNewNodeAdded(ref Last); } // , if(Last == null) throw new FormatException(); return Last.Root; // }
/// <summary> </summary> /// <param name="node"> </param> /// <param name="SeparatorTerm"> </param> /// <param name="DecimalSeparator"> </param> /// <param name="FrationPartTerm"> </param> /// <returns>, . , </returns> public static bool TryAddFractionPart(ref ExpressionTreeNode node, Term SeparatorTerm, char DecimalSeparator, Term FrationPartTerm) { var value = node as ConstValueNode; if(value == null) throw new ArgumentException(" "); var separator = SeparatorTerm as CharTerm; if(separator == null || separator.Value != DecimalSeparator) return false; var fraction = FrationPartTerm as NumberTerm; if(fraction == null) return false; var v_value = fraction.Value; if(v_value == 0) return true; node = new ConstValueNode(value.Value + v_value / Math.Pow(10, Math.Truncate(Math.Log10(v_value)) + 1)); return true; }
/// <summary> </summary> /// <param name="Last"> ( )</param> /// <param name="Node"> , </param> // ReSharper disable once CyclomaticComplexity public virtual void Combine([CanBeNull] ExpressionTreeNode Last, [NotNull] ExpressionTreeNode Node) { Contract.Requires(Node != null); if(Last == null) return; // , if(Node is CharNode) // - , { Last.LastRightChild = Node; // return; } var operator_node = Node as OperatorNode; // - if(operator_node != null) // ... { // : // // var parent_operator = Last as OperatorNode ?? Last.Parent as OperatorNode; if(parent_operator != null) // - ( )... { // - // op <- // | // op // / \ // null ? if(parent_operator.Left == null && parent_operator.Parent is OperatorNode) parent_operator = (OperatorNode)parent_operator.Parent; if(parent_operator.Left == null) // ... operator_node.Left = parent_operator; // else if(parent_operator.Right == null) // parent_operator.Right = Node; // else // { var priority = operator_node.Priority; // // , if(priority <= parent_operator.Priority) { // parent_operator = (OperatorNode)parent_operator.Parents // .TakeWhile(n => n is OperatorNode && priority <= ((OperatorNode)n).Priority) // .LastOrDefault() ?? parent_operator; // , // if(parent_operator.IsRoot) // - // , if(priority <= parent_operator.Priority) // operator_node.Left = parent_operator; else // { var parent = parent_operator.Parent; // parent.Right = Node; // operator_node.Left = parent_operator;// } } else // { // parent_operator = (OperatorNode)parent_operator.RightNodes // .TakeWhile(n => n is OperatorNode && n.Left != null && ((OperatorNode)n).Priority < priority) // .LastOrDefault() ?? parent_operator; // , // var right = parent_operator.Right; // parent_operator.Right = Node; // operator_node.Left = right; // } } } else // { var parent = Last.Parent; var is_left = Last.IsLeftSubtree; var is_right = Last.IsRightSubtree; operator_node.Left = Last; // if(is_left) parent.Left = operator_node; else if(is_right) parent.Right = operator_node; } return; // } // if(Last is OperatorNode) // { Last.Right = Node; // return; // } // // , - if(Last is ConstValueNode || (Last is ComputedBracketNode && Node is ComputedBracketNode)) { // var parent = Last.Parent; if(parent != null) // // parent.Right = new MultiplicationOperatorNode(Last, Node); else // // - , new MultiplicationOperatorNode(Last, Node); return; // . } Last.Right = Node; }
/// <summary> </summary> class StringTerm : Term { /// <summary> </summary> [NotNull] public string Name => f_Value; /// <summary> </summary> /// <param name="Name"> </param> public StringTerm([NotNull] string Name) : base(Name) { Contract.Requires(!string.IsNullOrEmpty(Name)); } /// <summary> , -</summary> /// <param name="Parser"></param> /// <param name="Expression"> </param> /// <returns> , Expression.Variable[Name]</returns> public override ExpressionTreeNode GetSubTree(ExpressionParser Parser, MathExpression Expression) => new VariableValueNode(Expression.Variable[Name]); } /// <summary> </summary> sealed class FunctionalTerm : FunctionTerm { /// <summary> </summary> [NotNull] public BlockTerm Parameters { get; set; } /// <summary> </summary> /// <param name="Header"> </param> /// <param name="Body"> </param> public FunctionalTerm([NotNull] FunctionTerm Header, [NotNull] BlockTerm Body) : base(Header.Name, Body) { Contract.Requires(Header != null); Contract.Requires(Body != null); Parameters = Header.Block; } /// <summary> </summary> /// <param name="Parser"></param> /// <param name="Expression"> </param> /// <returns> </returns> public override ExpressionTreeNode GetSubTree(ExpressionParser Parser, MathExpression Expression) => new FunctionalNode(this, Parser, Expression); public override string ToString() => $"{Name}{Parameters}{Block}"; }
/// <summary> </summary> /// <param name="Term"> </param> /// <param name="Parser"> </param> /// <param name="Expression"> </param> internal FunctionNode(FunctionTerm Term, ExpressionParser Parser, MathExpression Expression) : this(Term.Name) { var arg = Term.Block.GetSubTree(Parser, Expression); if(!(arg is FunctionArgumentNode)) if(arg is FunctionArgumentNameNode) arg = new FunctionArgumentNode((FunctionArgumentNameNode)arg); else if(arg is VariableValueNode) arg = new FunctionArgumentNode(null, arg); else if(arg is VariantOperatorNode && arg.Left is VariableValueNode) arg = new FunctionArgumentNode(((VariableValueNode)arg.Left).Name, arg.Right); else arg = new FunctionArgumentNode(null, arg); Right = arg; // - Function = Expression.Functions[Name, ArgumentsNames]; }
/// <summary> </summary> /// <param name="Expression"> </param> internal void ProcessVariables([NotNull] MathExpression Expression) { Contract.Requires(Expression != null); var tree_vars = Expression.Tree.Root.GetVariables().ToArray(); Expression.Variable .Where(v => !tree_vars.Contains(v)) .ToArray() .Foreach(v => Expression.Variable.Remove(v)); foreach(var variable in Expression.Variable.ToArray()) { if(f_Constans.ContainsKey(variable.Name)) { Expression.Variable.MoveToConstCollection(variable); variable.Value = f_Constans[variable.Name]; } OnVariableProcessing(variable); } }
/// <summary> </summary> /// <param name="Expression"> </param> [SuppressMessage("ReSharper", "CyclomaticComplexity")] internal void ProcessFunctions([NotNull] MathExpression Expression) { Contract.Requires(Expression != null); foreach(var function in Expression.Functions) switch(function.Name) { case "Sin": case "SIN": case "sin": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Sin); break; case "COS": case "Cos": case "cos": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Cos); break; case "TAN": case "Tan": case "tan": case "tn": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Tan); break; case "ATAN": case "ATan": case "Atan": case "atan": case "atn": case "Atn": if(function.Arguments.Length == 1) function.Delegate = new Func<double, double>(Math.Atan); else if(function.Arguments.Length == 2) function.Delegate = new Func<double, double, double>(Math.Atan2); else goto default; break; case "Atan2": case "atan2": if(function.Arguments.Length != 2) goto default; function.Delegate = new Func<double, double, double>(Math.Atan2); break; case "CTG": case "Ctg": case "ctg": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(x => 1 / Math.Tan(x)); break; case "Sign": case "sign": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(x => Math.Sign(x)); break; case "Abs": case "abs": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Abs); break; case "Exp": case "EXP": case "exp": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Exp); break; case "Sqrt": case "SQRT": case "√": case "sqrt": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Sqrt); break; case "log10": case "Log10": case "LOG10": case "lg": case "Lg": case "LG": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Log10); break; case "loge": case "Loge": case "LOGe": case "ln": case "Ln": case "LN": if(function.Arguments.Length != 1) goto default; function.Delegate = new Func<double, double>(Math.Log); break; case "log": case "Log": case "LOG": if(function.Arguments.Length != 2) goto default; function.Delegate = new Func<double, double, double>(Math.Log); break; default: var f = OnFunctionFind(function.Name, function.Arguments); if(f == null) throw new NotSupportedException($" {function.Name} "); function.Delegate = f; break; } }
var parser = new ExpressionParser(); parser .FindFunction += (s, e) => { if(e.SignatureEqual(name: "G", ArgumentsCount: 1)) e.Function = new Func<double, double>(x => 2 * Math.Cos(x)); }; var expr = parser.Parse(@"Int[x=-10..10;dx=0.05]{A*cos(2x) + G(x/2)}/A + 1"); expr.Variable["A"] = 5; var y = expr.Compute(); //y = 0.30928806858920344 var f = expr.Compile(); var y2 = f(); //y = 0.30928806858920344
Source: https://habr.com/ru/post/281495/
All Articles