
- •Оглавление
- •Часть 1
- •Контрольные примеры
- •Выводы по первой части курсового проекта:
- •Приложение 1
- •Часть 2 Задание (вариант 7-2,7):
- •Краткая справка по теме:
- •Формализация задачи:
- •Листинг программы
- •Результаты тестирования программы:
- •Выводы по второй части кп:
- •Приложение 2
- •Часть 3 Задание(вариант г42-2,г-42):
- •Краткая справка по заданию
- •Формализация задачи
- •Листинг программы:
- •Контрольные примеры
- •Выводы по третьей части курсового проекта:
- •Приложение 3
- •Часть 4 Задание
- •Формализация задачи:
- •Листинг программы
- •Контрольные примеры:
- •Выводы по 4-ой части курсового проекта:
- •Приложение 4
- •Литература
Формализация задачи:
Рис. 4.2 UMLдиаграмма
- Класс Parser парсит строчку. Основные функции парсера – TextParser, которая записывает строчку в форме обратной польской записи и выделяет из нее переменные. Все это хранится в полях класса Parser.
- Класс Methods – основной алгоритм программы. Используется для поиска стационарной точки критерия оптимальности.
- Eval класса определяет его собственный класс исключений, EvalException , который включает в себя поддержку для отслеживания позиции столбца, где произошло исключение. Эта информация может быть использована, чтобы помочь пользователю определить причину ошибки.
Листинг программы
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace _2Face_CourseWork04
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace _2Face_CourseWork04
{
public partial class Form1 : Form
{
Methods nw;
/**
* Конструктор
*/
public Form1()
{
this.nw = new Methods();
InitializeComponent();
}
/**
* Обработчик кнопки "Расчёт"
*/
private void button1_Click(object sender, EventArgs e)
{
try
{
// берём значения из формы
double a, b;
a = int.Parse(this.textBox3.Text);
b = int.Parse(this.textBox4.Text);
if (a >= b)
throw new Exception("Указан неверный промежуток [a,b]");
// задаём функцию
this.nw.SetFunction(this.textBox1.Text);
// вычисляем и выводим результат
this.textBox2.Text = this.nw.MethodSecant(a, b).ToString();
}
catch (Exception x)
{
MessageBox.Show(x.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
Methods.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SoftCircuits;
namespace _2Face_CourseWork04
{
class Methods
{
Eval f;
double _x;
string _f;
/**
* Конструктор
*/
public Methods()
{
// инициализируем парсер
f = new Eval();
}
/**
* Установить функцию для парсера
*/
public void SetFunction(string s)
{
this._f = s;
}
/**
* Вычисление f(x)
*/
double GetF(double x)
{
this._x = x;
string _s = _f.Replace("x", "("+x.ToString().Replace(',','.')+")");
return this.f.Execute( _s );
}
/**
* Метод секущих (хорд)
*/
public double MethodSecant( double xa, double xb, double epsilon = 0.00001)
{
double xlast;
double x = 0;
if (this.GetF(xa) * this.GetF(xb) >= 0)
{
throw new ArgumentException("f(a) и f(b) должны иметь разыне знаки!");
}
var iter = 0;
do
{
xlast = x;
x = xb - this.GetF(xb) * (xb - xa) / (this.GetF(xb) - this.GetF(xa));
if (this.GetF(x) * this.GetF(xa) > 0)
{
xa = x;
}
else
{
xb = x;
}
iter++;
}
while (Math.Abs(x - xlast) > epsilon || (xb - xa) <= epsilon || iter == 1000);
return x;
}
}
}
Eval.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SoftCircuits
{
/// <summary>
/// Custom exception for evaluation errors
/// </summary>
public class EvalException : Exception
{
/// <summary>
/// Zero-base position in expression where exception occurred
/// </summary>
public int Column { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="message">Message that describes this exception</param>
/// <param name="position">Position within expression where exception occurred</param>
public EvalException(string message, int position)
: base(message)
{
Column = position;
}
/// <summary>
/// Gets the message associated with this exception
/// </summary>
public override string Message
{
get
{
return String.Format("{0} (column {1})", base.Message, Column + 1);
}
}
}
public enum SymbolStatus
{
OK,
UndefinedSymbol,
}
// ProcessSymbol arguments
public class SymbolEventArgs : EventArgs
{
public string Name { get; set; }
public double Result { get; set; }
public SymbolStatus Status { get; set; }
}
public enum FunctionStatus
{
OK,
UndefinedFunction,
WrongParameterCount,
}
// ProcessFunction arguments
public class FunctionEventArgs : EventArgs
{
public string Name { get; set; }
public List<double> Parameters { get; set; }
public double Result { get; set; }
public FunctionStatus Status { get; set; }
}
/// <summary>
/// Expression evaluator class
/// </summary>
public class Eval
{
// Event handers
public delegate void ProcessSymbolHandler(object sender, SymbolEventArgs e);
public delegate void ProcessFunctionHandler(object sender, FunctionEventArgs e);
public event ProcessSymbolHandler ProcessSymbol;
public event ProcessFunctionHandler ProcessFunction;
// Token state enums
protected enum State
{
None = 0,
Operand = 1,
Operator = 2,
UnaryOperator = 3
}
// Error messages
protected string ErrInvalidOperand = "Invalid operand";
protected string ErrOperandExpected = "Operand expected";
protected string ErrOperatorExpected = "Operator expected";
protected string ErrUnmatchedClosingParen = "Closing parenthesis without matching open parenthesis";
protected string ErrMultipleDecimalPoints = "Operand contains multiple decimal points";
protected string ErrUnexpectedCharacter = "Unexpected character encountered \"{0}\"";
protected string ErrUndefinedSymbol = "Undefined symbol \"{0}\"";
protected string ErrUndefinedFunction = "Undefined function \"{0}\"";
protected string ErrClosingParenExpected = "Closing parenthesis expected";
protected string ErrWrongParamCount = "Wrong number of function parameters";
// To distinguish it from a minus operator,
// we'll use a character unlikely to appear
// in expressions to signify a unary negative
protected const string UnaryMinus = "\x80";
//
public Eval()
{
}
/// <summary>
/// Evaluates the given expression and returns the result
/// </summary>
/// <param name="expression">The expression to evaluate</param>
/// <returns></returns>
public double Execute(string expression)
{
return ExecuteTokens(TokenizeExpression(expression));
}
/// <summary>
/// Converts a standard infix expression to list of tokens in
/// postfix order.
/// </summary>
/// <param name="expression">Expression to evaluate</param>
/// <returns></returns>
protected List<string> TokenizeExpression(string expression)
{
List<string> tokens = new List<string>();
Stack<string> stack = new Stack<string>();
State state = State.None;
int parenCount = 0;
string temp;
TextParser parser = new TextParser(expression);
while (!parser.EndOfText)
{
if (Char.IsWhiteSpace(parser.Peek()))
{
// Ignore spaces, tabs, etc.
}
else if (parser.Peek() == '(')
{
// Cannot follow operand
if (state == State.Operand)
throw new EvalException(ErrOperatorExpected, parser.Position);
// Allow additional unary operators after "("
if (state == State.UnaryOperator)
state = State.Operator;
// Push opening parenthesis onto stack
stack.Push(parser.Peek().ToString());
// Track number of parentheses
parenCount++;
}
else if (parser.Peek() == ')')
{
// Must follow operand
if (state != State.Operand)
throw new EvalException(ErrOperandExpected, parser.Position);
// Must have matching open parenthesis
if (parenCount == 0)
throw new EvalException(ErrUnmatchedClosingParen, parser.Position);
// Pop all operators until matching "(" found
temp = stack.Pop();
while (temp != "(")
{
tokens.Add(temp);
temp = stack.Pop();
}
// Track number of parentheses
parenCount--;
}
else if ("+-*/".Contains(parser.Peek()))
{
// Need a bit of extra code to support unary operators
if (state == State.Operand)
{
// Pop operators with precedence >= current operator
int currPrecedence = GetPrecedence(parser.Peek().ToString());
while (stack.Count > 0 && GetPrecedence(stack.Peek()) >= currPrecedence)
tokens.Add(stack.Pop());
stack.Push(parser.Peek().ToString());
state = State.Operator;
}
else if (state == State.UnaryOperator)
{
// Don't allow two unary operators together
throw new EvalException(ErrOperandExpected, parser.Position);
}
else
{
// Test for unary operator
if (parser.Peek() == '-')
{
// Push unary minus
stack.Push(UnaryMinus);
state = State.UnaryOperator;
}
else if (parser.Peek() == '+')
{
// Just ignore unary plus
state = State.UnaryOperator;
}
else
{
throw new EvalException(ErrOperandExpected, parser.Position);
}
}
}
else if (Char.IsDigit(parser.Peek()) || parser.Peek() == '.')
{
if (state == State.Operand)
{
// Cannot follow other operand
throw new EvalException(ErrOperatorExpected, parser.Position);
}
// Parse number
temp = ParseNumberToken(parser);
tokens.Add(temp);
state = State.Operand;
continue;
}
else
{
double result;
// Parse symbols and functions
if (state == State.Operand)
{
// Symbol or function cannot follow other operand
throw new EvalException(ErrOperatorExpected, parser.Position);
}
if (!(Char.IsLetter(parser.Peek()) || parser.Peek() == '_'))
{
// Invalid character
temp = String.Format(ErrUnexpectedCharacter, parser.Peek());
throw new EvalException(temp, parser.Position);
}
// Save start of symbol for error reporting
int symbolPos = parser.Position;
// Parse this symbol
temp = ParseSymbolToken(parser);
// Skip whitespace
parser.MovePastWhitespace();
// Check for parameter list
if (parser.Peek() == '(')
{
// Found parameter list, evaluate function
result = EvaluateFunction(parser, temp, symbolPos);
}
else
{
// No parameter list, evaluate symbol (variable)
result = EvaluateSymbol(temp, symbolPos);
}
// Handle negative result
if (result < 0)
{
stack.Push(UnaryMinus);
result = Math.Abs(result);
}
tokens.Add(result.ToString());
state = State.Operand;
continue;
}
parser.MoveAhead();
}
// Expression cannot end with operator
if (state == State.Operator || state == State.UnaryOperator)
throw new EvalException(ErrOperandExpected, parser.Position);
// Check for balanced parentheses
if (parenCount > 0)
throw new EvalException(ErrClosingParenExpected, parser.Position);
// Retrieve remaining operators from stack
while (stack.Count > 0)
tokens.Add(stack.Pop());
return tokens;
}
/// <summary>
/// Parses and extracts a numeric value at the current position
/// </summary>
/// <param name="parser">TextParser object</param>
/// <returns></returns>
protected string ParseNumberToken(TextParser parser)
{
bool hasDecimal = false;
int start = parser.Position;
while (Char.IsDigit(parser.Peek()) || parser.Peek() == '.')
{
if (parser.Peek() == '.')
{
if (hasDecimal)
throw new EvalException(ErrMultipleDecimalPoints, parser.Position);
hasDecimal = true;
}
parser.MoveAhead();
}
// Extract token
string token = parser.Extract(start, parser.Position);
if (token == ".")
throw new EvalException(ErrInvalidOperand, parser.Position - 1);
return token;
}
/// <summary>
/// Parses and extracts a symbol at the current position
/// </summary>
/// <param name="parser">TextParser object</param>
/// <returns></returns>
protected string ParseSymbolToken(TextParser parser)
{
int start = parser.Position;
while (Char.IsLetterOrDigit(parser.Peek()) || parser.Peek() == '_')
parser.MoveAhead();
return parser.Extract(start, parser.Position);
}
/// <summary>
/// Evaluates a function and returns its value. It is assumed the current
/// position is at the opening parenthesis of the argument list.
/// </summary>
/// <param name="parser">TextParser object</param>
/// <param name="name">Name of function</param>
/// <param name="pos">Position at start of function</param>
/// <returns></returns>
protected double EvaluateFunction(TextParser parser, string name, int pos)
{
double result = default(double);
// Parse function parameters
List<double> parameters = ParseParameters(parser);
// We found a function reference
FunctionStatus status = FunctionStatus.UndefinedFunction;
if (ProcessFunction != null)
{
FunctionEventArgs args = new FunctionEventArgs();
args.Name = name;
args.Parameters = parameters;
args.Result = result;
args.Status = FunctionStatus.OK;
ProcessFunction(this, args);
result = args.Result;
status = args.Status;
}
if (status == FunctionStatus.UndefinedFunction)
throw new EvalException(String.Format(ErrUndefinedFunction, name), pos);
if (status == FunctionStatus.WrongParameterCount)
throw new EvalException(ErrWrongParamCount, pos);
return result;
}
/// <summary>
/// Evaluates each parameter of a function's parameter list and returns
/// a list of those values. An empty list is returned if no parameters
/// were found. It is assumed the current position is at the opening
/// parenthesis of the argument list.
/// </summary>
/// <param name="parser">TextParser object</param>
/// <returns></returns>
protected List<double> ParseParameters(TextParser parser)
{
// Move past open parenthesis
parser.MoveAhead();
// Look for function parameters
List<double> parameters = new List<double>();
parser.MovePastWhitespace();
if (parser.Peek() != ')')
{
// Parse function parameter list
int paramStart = parser.Position;
int parenCount = 1;
while (!parser.EndOfText)
{
if (parser.Peek() == ',')
{
// Note: Ignore commas inside parentheses. They could be
// from a parameter list for a function inside the parameters
if (parenCount == 1)
{
parameters.Add(EvaluateParameter(parser, paramStart));
paramStart = parser.Position + 1;
}
}
if (parser.Peek() == ')')
{
parenCount--;
if (parenCount == 0)
{
parameters.Add(EvaluateParameter(parser, paramStart));
break;
}
}
else if (parser.Peek() == '(')
{
parenCount++;
}
parser.MoveAhead();
}
}
// Make sure we found a closing parenthesis
if (parser.Peek() != ')')
throw new EvalException(ErrClosingParenExpected, parser.Position);
// Move past closing parenthesis
parser.MoveAhead();
// Return parameter list
return parameters;
}
/// <summary>
/// Extracts and evaluates a function parameter and returns its value. If an
/// exception occurs, it is caught and the column is adjusted to reflect the
/// position in original string, and the exception is rethrown.
/// </summary>
/// <param name="parser">TextParser object</param>
/// <param name="paramStart">Column where this parameter started</param>
/// <returns></returns>
protected double EvaluateParameter(TextParser parser, int paramStart)
{
try
{
// Extract expression and evaluate it
string expression = parser.Extract(paramStart, parser.Position);
return Execute(expression);
}
catch (EvalException ex)
{
// Adjust column and rethrow exception
ex.Column += paramStart;
throw ex;
}
}
/// <summary>
/// This method evaluates a symbol name and returns its value.
/// </summary>
/// <param name="name">Name of symbol</param>
/// <param name="pos">Position at start of symbol</param>
/// <returns></returns>
protected double EvaluateSymbol(string name, int pos)
{
double result = default(double);
// We found a symbol reference
SymbolStatus status = SymbolStatus.UndefinedSymbol;
if (ProcessSymbol != null)
{
SymbolEventArgs args = new SymbolEventArgs();
args.Name = name;
args.Result = result;
args.Status = SymbolStatus.OK;
ProcessSymbol(this, args);
result = args.Result;
status = args.Status;
}
if (status == SymbolStatus.UndefinedSymbol)
throw new EvalException(String.Format(ErrUndefinedSymbol, name), pos);
return result;
}
/// <summary>
/// Evaluates the given list of tokens and returns the result.
/// Tokens must appear in postfix order.
/// </summary>
/// <param name="tokens">List of tokens to evaluate.</param>
/// <returns></returns>
protected double ExecuteTokens(List<string> tokens)
{
Stack<double> stack = new Stack<double>();
double tmp, tmp2;
foreach (string token in tokens)
{
// Is this a value token?
int count = token.Count(c => Char.IsDigit(c) || c == '.');
if (count == token.Length)
{
stack.Push(double.Parse(token.Replace('.',',')));
}
else if (token == "+")
{
stack.Push(stack.Pop() + stack.Pop());
}
else if (token == "-")
{
tmp = stack.Pop();
tmp2 = stack.Pop();
stack.Push(tmp2 - tmp);
}
else if (token == "*")
{
stack.Push(stack.Pop() * stack.Pop());
}
else if (token == "/")
{
tmp = stack.Pop();
tmp2 = stack.Pop();
stack.Push(tmp2 / tmp);
}
else if (token == UnaryMinus)
{
stack.Push(-stack.Pop());
}
}
// Remaining item on stack contains result
return (stack.Count > 0) ? stack.Pop() : 0.0;
}
/// <summary>
/// Returns a value that indicates the relative precedence of
/// the specified operator
/// </summary>
/// <param name="s">Operator to be tested</param>
/// <returns></returns>
protected int GetPrecedence(string s)
{
switch (s)
{
case "+":
case "-":
return 1;
case "*":
case "/":
return 2;
case "^":
return 3;
case UnaryMinus:
return 10;
}
return 0;
}
}
}
TextParser.cs
// Copyright (c) 2010 Jonathan Wood
using System;
namespace SoftCircuits
{
public class TextParser
{
private string _text;
private int _pos;
public string Text { get { return _text; } }
public int Position { get { return _pos; } }
public int Remaining { get { return _text.Length - _pos; } }
public static char NullChar = (char)0;
public TextParser()
{
Reset(null);
}
public TextParser(string text)
{
Reset(text);
}
/// <summary>
/// Resets the current position to the start of the current document
/// </summary>
public void Reset()
{
_pos = 0;
}
/// <summary>
/// Sets the current document and resets the current position to the start of it
/// </summary>
/// <param name="html"></param>
public void Reset(string text)
{
_text = (text != null) ? text : String.Empty;
_pos = 0;
}
/// <summary>
/// Indicates if the current position is at the end of the current document
/// </summary>
public bool EndOfText
{
get { return (_pos >= _text.Length); }
}
/// <summary>
/// Returns the character at the current position, or a null character if we're
/// at the end of the document
/// </summary>
/// <returns>The character at the current position</returns>
public char Peek()
{
return Peek(0);
}
/// <summary>
/// Returns the character at the specified number of characters beyond the current
/// position, or a null character if the specified position is at the end of the
/// document
/// </summary>
/// <param name="ahead">The number of characters beyond the current position</param>
/// <returns>The character at the specified position</returns>
public char Peek(int ahead)
{
int pos = (_pos + ahead);
if (pos < _text.Length)
return _text[pos];
return NullChar;
}
/// <summary>
/// Extracts a substring from the specified position to the end of the text
/// </summary>
/// <param name="start"></param>
/// <returns></returns>
public string Extract(int start)
{
return Extract(start, _text.Length);
}
/// <summary>
/// Extracts a substring from the specified range of the current text
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public string Extract(int start, int end)
{
return _text.Substring(start, end - start);
}
/// <summary>
/// Moves the current position ahead one character
/// </summary>
public void MoveAhead()
{
MoveAhead(1);
}
/// <summary>
/// Moves the current position ahead the specified number of characters
/// </summary>
/// <param name="ahead">The number of characters to move ahead</param>
public void MoveAhead(int ahead)
{
_pos = Math.Min(_pos + ahead, _text.Length);
}
/// <summary>
/// Moves to the next occurrence of the specified string
/// </summary>
/// <param name="s">String to find</param>
/// <param name="ignoreCase">Indicates if case-insensitive comparisons are used</param>
public void MoveTo(string s, bool ignoreCase = false)
{
_pos = _text.IndexOf(s, _pos, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
if (_pos < 0)
_pos = _text.Length;
}
/// <summary>
/// Moves to the next occurrence of the specified character
/// </summary>
/// <param name="c">Character to find</param>
public void MoveTo(char c)
{
_pos = _text.IndexOf(c, _pos);
if (_pos < 0)
_pos = _text.Length;
}
/// <summary>
/// Moves to the next occurrence of any one of the specified
/// characters
/// </summary>
/// <param name="chars">Array of characters to find</param>
public void MoveTo(char[] chars)
{
_pos = _text.IndexOfAny(chars, _pos);
if (_pos < 0)
_pos = _text.Length;
}
/// <summary>
/// Moves to the next occurrence of any character that is not one
/// of the specified characters
/// </summary>
/// <param name="chars">Array of characters to move past</param>
public void MovePast(char[] chars)
{
while (IsInArray(Peek(), chars))
MoveAhead();
}
/// <summary>
/// Determines if the specified character exists in the specified
/// character array.
/// </summary>
/// <param name="c">Character to find</param>
/// <param name="chars">Character array to search</param>
/// <returns></returns>
protected bool IsInArray(char c, char[] chars)
{
foreach (char ch in chars)
{
if (c == ch)
return true;
}
return false;
}
/// <summary>
/// Moves the current position to the first character that is part of a newline
/// </summary>
public void MoveToEndOfLine()
{
char c = Peek();
while (c != '\r' && c != '\n' && !EndOfText)
{
MoveAhead();
c = Peek();
}
}
/// <summary>
/// Moves the current position to the next character that is not whitespace
/// </summary>
public void MovePastWhitespace()
{
while (Char.IsWhiteSpace(Peek()))
MoveAhead();
}
}
}