http://antibotan.com/ - Всеукраїнський студентський архів СОДЕРЖАНИЕ ВВЕДЕНИЕ .................................................................................................................. 4 ПОСТАНОВКА ЗАДАЧИ .......................................................................................... 5 РАЗРАБОТКА СТРУКТУРЫ ПРОГРАММЫ ......................................................... 6 СПЕЦИФИКАЦИЯ ..................................................................................................... 7 ОПИСАНИЕ АЛГОРИТМОВ РЕАЛИЗАЦИИ ........................................................ 13 РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ ......................................................................... 16 ПРИМЕР РАБОТЫ ПРОГРАММЫ .......................................................................... 17 ЗАКЛЮЧЕНИЕ ........................................................................................................... 18 БИБЛИОГРАФИЧЕСКИЙ СПИСОК ....................................................................... 19 4 http://antibotan.com/ - Всеукраїнський студентський архів Задание на курсовой проект По дисциплине «Системное программирование» Вариант 1. Реализовать формульный редактор с возможностью гибкого добавления новых функций и операторов. Особых требований к графическому интерфейсу нет, поскольку основная задача – сделать гибкую программную модель. Минимальный функционал: пользователь вводит формулу и значение переменных (на усмотрение разработчика – диапазон значений), после чего нажимает кнопку «Рассчитать». Результат работы отображается в виде таблицы или графика функции. Для простоты можно рассматривать задачу для функции от одной переменной и считать, что каждый оператор обязан быть заключен в скобки. При вводе предусмотреть корректную обработку ошибок: проверка баланса скобок, ошибки деления на ноль и проч. Среда разработки: Visual Studio 2010, язык программирования: C++ или C#. 5 http://antibotan.com/ - Всеукраїнський студентський архів ВВЕДЕНИЕ Библиотека динамической компоновки (DLL) является исполняемым файлом, который выполняет функции общей библиотеки. Динамическая компоновка представляет способ вызова функции, который не является частью исполняемого кода. Исполняемый код функции расположен в библиотеке DLL, которая содержит несколько компилированных, связанных и отдельно сохраненных функций в используемых процессах. Библиотеки DLL часто упрощают процесс общего доступа к данным и источникам. Многочисленные приложения могут иметь одновременный доступ к нескольким содержаниям одной копии DLL в памяти. Основные преимущества DLL выглядят следующим образом: Все приложения могут совместно использовать одну копию на диске. Исполняемые файлы приложений хранятся меньшего размера. Они позволяют больших проектов разбить. Только разработчики приложения и библиотеки DLL необходимо согласовать интерфейс между их соответствующих частей. Этот интерфейс экспортируемую библиотекой DLL. Разработчики библиотеки DLL можно обновить DLL — возможно, чтобы сделать более эффективным или исправления ошибки — без необходимости обновления все приложения, использующие его, при условии, экспортированный интерфейс библиотеки DLL не изменяется. Библиотеки DLL можно использовать для добавления функций и команд 6 что http://antibotan.com/ - Всеукраїнський студентський архів ПОСТАНОВКА ЗАДАЧИ Программа «Редактор формул» позволяет строить графики функций одной переменной вида F(x). Анализатор формул, который распознает введенную функцию по строке, например sin(x)/x+0,05*x и интерпретатор подключаются динамически, также программа автоматически определяет область допустимых значений переменной и проверяет введенную функцию на корректность записи. Язык реализации программного продукта – Visual C++ .NET, среда разработки – Microsoft Visual Studio .NET 2010. 7 http://antibotan.com/ - Всеукраїнський студентський архів РАЗРАБОТКА СТРУКТУРЫ ПРОГРАММЫ. Для реализации поставленной задачи необходимо спроектировать dll библиотеку, которая позволит вычислять значение выражения по введенной строке. При разработке dll для синтаксического анализа вводимой функции была создана иерархия классов лексем: бинарные операции, унарные операции, число, функции. Кроме того, были созданы классы сканера Scaner и интерпретатора Interpreter. Сканер проверяет введенную строку, содержащую математическую функцию на наличие ошибок и преобразует его в польскую запись, заменяет переменные и константы конкретными значениями. Интерпретатор производит вычисление выражения, используя польскую запись выражения, созданную сканером. Dll будем подключать динамически используя явное связывание. Хотя общем случае не рекомендуется размещать классы в библиотеках, подключаемых явно. Но приемлемым можно считать подход, который исповедует COM, при котором объекты класса создаются и разрушаются внутри DLL (для этого используются экспортируемые глобальные функции), а сам класс содержит исключительно виртуальные методы. При явном связывании приложения должны выполнять вызов функции для явной загрузки библиотеки DLL во время выполнения. Чтобы выполнить явное связывание с библиотекой DLL, приложение должно выполнить следующие действия. Вызвать функцию LoadLibrary (или аналогичную функцию) для загрузки библиотеки DLL и получения дескриптора модуля. Вызвать функцию GetProcAddress, чтобы получить указатель для каждой экспортируемой функции, вызываемой приложением. Поскольку приложения вызывают функции библиотек DLL с помощью указателя, 8 http://antibotan.com/ - Всеукраїнський студентський архів компилятор не создает внешних ссылок, поэтому нет необходимости выполнять связывание с библиотекой импорта. Вызвать функцию FreeLibrary по завершении всех действий с библиотекой DLL. Для реализации непосредственно построения графика был создан класс AzGraphCtrl, который отображает координатную сетку и оси в заданных пределах, создавая окно, строит график функции и тд. СПЕЦИФИКАЦИЯ Класс «CMyData» Рекурсивная структура данных, носит вспомогательный характер, элемент связанного списка. Поля данных: CMyData * m_next – указатель на следующий элемент; Класс «CStack» Структура на основе списка, необходима для формирования польской записи выражения и вычисления результата. Поля данных: CMyData * pstack – указатель на элемент списка. Методы: void Push(double x) – проталкивание элемента в стек; double Pop(void) – выталкивание элемента из стека; double GetResult(void) – проверяет на пустоту стека и вызывает Рор() ; void Clear(void) – очистка стека; Базовый класс лексем «CLexem» Прежде чем обрабатывать выражение, необходимо разбить его на составляющие части (лексемы). Лексема — минимальная, не делимая часть выражения. Поля данных: CLexem * next – указатель на следующую лексему. 9 http://antibotan.com/ - Всеукраїнський студентський архів Методы: virtual void Execute(CStack* st) – извлекает из стека данные, и преобразованные возвращает в стек. Класс вещественной константы «CReal» производный от CLexem Поля данных: double m_value – вещественное число. Методы: virtual void Execute(CStack* st) – помещает данное число в стек. Класс унарных операторов «CUnareOp» производный от CLexem Методы: virtual void Execute(CStack* st) –извлекает данное из стека, преобразует его в функции Eval и помещает в стек. virtual double Eval(double x) – возвращает положительное или отрицательное число в зависимости от лексемы. Класс бинарных операторов «CBinareOp» производный от CLexem Методы: virtual void Execute(CStack* st) – извлекает из стека два верхних значения, передает их в Eval и помещает в стек результат. virtual double Eval (double x, double y) – выполняет операции умножения, деления, возведения в степень, сложения или вычитания над данными, возвращает результат. Класс математических функций «CFunction» производный от CLexem Методы: virtual void Execute(CStack* st) –извлекает данное из стека, преобразует его в функции Eval и помещает в стек. virtual double Eval(double x) – возвращает значение соответствующей функции от х. Также были разработаны производные классы для каждого вида лексем, которые соответствуют математическим операциям или функциям. В каждом их 10 http://antibotan.com/ - Всеукраїнський студентський архів этих классов есть перегруженная виртуальная функция Eval, которая выполняет необходимую операцию над данными и возвращает результат операции или сообщает об ошибке. Классы унарных операторов: class CUnMinus : public CUnareOp class CUnPlus : public CUnareOp Классы бинарных операторов class CPlus : public CBinareOp class CMinus : public CBinareOp class CMult : public CBinareOp class CDiv : public CBinareOp class Extent : public CBinareOp Классы математических функций class CSin : public CFunction class CCos : public CFunction class CTg : public CFunction …. class CArcCTg : public CFunction class Cln : public CFunction class Clog : public CFunction class CExp: public CFunction сlass CSqrt: public CFunction Класс « CScaner» Сканер проверяет введенную строку, содержащую математическую функцию на наличие ошибок и преобразует его в польскую запись, заменяет переменные и константы конкретными значениями. Поля данных: CLexem* last – предыдущая лексема; CLexem* polska – головной элемент списка; int pos– текущий номер элемента в строке; 11 http://antibotan.com/ - Всеукраїнський студентський архів сhar expression[256] – входная строка, функция; bool errorflag – логическая переменная, отслеживающая появление ошибки; сhar errorstring – строка сообщения об ощибки; int variable – значение переменной; Методы: void RemoveChain(void) – удаляет список лексем; сhar EatSpaces(сhar str) – преобразует входную строку, удаляя из нее пробелы, а также заменяя название функций в удобную для анализа форму, например, sinx преобразуется в sx, где s – обозначение синуса, заменяет переменные и константы на конкретные значения; CLexem* Scan(сhar str) – преобразует строку в обратную польскую запись методом рекурсивного спуска основанного на связанном списке или выводит сообщение об ошибке, связанной с лишними скобками, недопустимым знаком, или недостающим знаком операции; bool ScanFormula(void); функции участвуют в рекурсивном bool ScanElement(void); спуске, вызываются поочередно в bool ScanFormFunct(void); зависимости от правил математики, bool ScanFactor(void); т.е приоритета операций. CBinareOp* GetMultOp(void) – создает экземпляр класса CMult, CDiv, или Extent в зависимости от текущего символа операции и сдвигает позицию, если текущий символ не относится к этим операция, то позиция сохраняется. CBinareOp* GetAddOp(void) – работает аналогично только проверяются и создаются бинарные операции сложения и вычитания; CUnareOp* GetUnareOp(void) – проверяет и создает экземпляры унарных классов + и –; CFunction* GetFunct(void) – проверка и создание экземпляров класса математических функций; int ScanDigit(void) – проверяет символ является ли он цифрой и возвращает соответствующие значение; CReal* GetReal(void) – создает из цифр вещественное число ; 12 http://antibotan.com/ - Всеукраїнський студентський архів bool ScanPoint(void) – проверяет является ли текущий символ точкой; void AddInChain(CLexem* lexem) – добавляет элемент в связанный список; bool EndOfString(void) – проверка на достижение конца строки; Char ReadChar(void) – возвращает символ соответствующий текущей позиции; bool ScanRPar(void), bool ScanLPar(void) – проверяет является ли символ «(» или «)». Класс «CInterpreter» Производит вычисление выражения, используя польскую запись выражения, созданную сканером. Поля данных: CStack *results – стек результатов. Методы: double Run(CLexem * polska) – преобразует польскую запись, последовательно занося в стек результатов значения вычисления операций, в итоге в стеке находится одно значение, которое является результатом вычисления всего выражения. Класс «AzGraphCtrl» Создает окно, в котором непосредственно осуществляется построение графика функции. Поля данных: HINSTANCE m_hInst – идентификатор экземпляра приложения DATABASEFORGRAPH db – структура содержащая основную информации о всех цветах, параметрах построения графика, координатной сетки и тд. Методы: bool Create(HWND hParent, HMENU hID, RECT Rct) – создает окно графика в заданном прямоугольнике; bool Create(HWND hParent, HMENU hID, int x, int y, int cx, int cy) – создает окно графика в заданных координатах с заданными размерами; BOOL SetWindowPos(int X, int Y, int cx, int cy, UINT uFlags) – изменяет позицию и размеры окна; 13 http://antibotan.com/ - Всеукраїнський студентський архів void SetMinMaxX(double min, double max, BOOL bRedraw = TRUE) – определяет диапазон отображения графика по оси X; void SetCountLineGridX(UINT nLine, BOOL bRedraw = TRUE) –задает количество линий сетки по оси X; void SetGridYColor(COLORREF clr, BOOL bRedraw = TRUE) – задает цвет линий сетки по оси Y void SetCountData(UINT nData) – определяет количество точек графика void SetAllData(DOUBLE* pDX, DOUBLE* pDY) – передача массивов точек графика; BOOL Ploting(DOUBLE dx, DOUBLE dy, BOOL bDrawing = TRUE) – вывод графика поточечно; void Clear(BOOL bRedraw = TRUE) – очищает область графика; void Update()– Обновляет область графика СТРУКТУРНАЯ СХЕМА ОТНОШЕНИЙ В СПЕЦИФИКАЦИИ КЛАССОВ CLexem CUnareOp CBinareOp CReal CFunction CUnPlus CPlus CSin CUnMinus CMinus CCos CMult … CDiv CSqrt Рисунок 1– Иерархия классов лексем. CScaner CInterpreter CStack CLexem* Scan double Run 14 CLexem CMyData http://antibotan.com/ - Всеукраїнський студентський архів Рисунок 2 – Диаграмма классов. ОПИСАНИЕ АЛГОРИТМОВ РЕАЛИЗАЦИИ Синтаксический разбор выражения. Формирование польской записи методом рекурсивного спуска. Реализация данного алгоритма представляет собой несколько функций, последовательно вызывающих друг друга. Началом обработки выражения становится вызов функции ScanFormula(), которая обрабатывает сложение и вычитание. Функция ScanFormula() вызывает функцию ScanElement(), обрабатывающую знаки деления и умножения. А функция ScanElement() вызывает в свою очередь функцию ScanFormFunct(), обрабатывающую математические функции. Далее функция ScanFormFunct() вызывает функцию ScanFactor(), обрабатывающую переменные (или числа) и скобки. Если функция ScanFactor() получает открывающую скобку, она вызывает функцию ScanFormula() (т.е. все начинается сначала) и ожидает закрывающей скобки, когда управление вновь возвращается к ней. Если она не дожидается закрывающей скобки, это означает, что в выражении содержится синтаксическая ошибка. Если функция ScanFactor () получает переменную, то помещает ее в список. CLexem* CScaner::Scan(String ^str) { errorflag=false; errorstring=""; expression=EatSpaces(str); //удаляет пробелы и преобразует согласно Pos::set(0); //синтаксису if (!ScanFormula()) { if (!errorflag) 15 http://antibotan.com/ - Всеукраїнський студентський архів { errorflag=true; errorstring="Недопустимый символ либо требуется знак операции"; } RemoveChain(); // удаляет список return nullptr; } if (!EndOfString()) { errorflag=true; errorstring="Недопустимый символ либо требуется знак операции"; RemoveChain(); return nullptr; } return polska; } bool CScaner::ScanFormula() { CLexem * cur; cur=GetUnareOp(); // унарные опреции +/if (!ScanElement()) { if (cur) delete cur; return false; } if (cur) AddInChain(cur); // добавить в список while (1) { cur = GetAddOp(); // бинарные операции + и if (!cur) return true; if (!ScanElement()) { delete cur; return false; } AddInChain(cur); // добавить в список } } bool CScaner::ScanElement() { CLexem * cur; if (!ScanFormFunct())return false; while (1) { cur=GetMultOp(); // операции *,/,возвести в степень if (!cur) return true; if (!ScanFormFunct()) { delete cur; return false; } AddInChain(cur); // добавить в список } } bool CScaner::ScanFormFunct() { CLexem * cur; 16 http://antibotan.com/ - Всеукраїнський студентський архів cur=GetFunct(); // математические функции if (!ScanFactor()) { delete cur; return false; } if (cur) AddInChain(cur); // добавить в список return true; } bool CScaner::ScanFactor() { CLexem * cur; cur = GetReal(); if (cur) { AddInChain(cur); return true; } if (!ScanLPar()) { errorflag=true; errorstring="Требуется выражение, возможно лишняя скобка"; return false; } if (!ScanFormula()) return false; if (!ScanRPar()) { errorflag=true; errorstring="Требуется `)`"; return false; } return true; } 17 http://antibotan.com/ - Всеукраїнський студентський архів РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ При открытии программы появляется окно, которое содержит область для построения графика с расчерченной координатной плоскостью. Чтобы построить необходимый график функции или изменить отрезок, на котором построен график, необходимо внести соответствующие изменения в поля формы, которые расположены слева от координатной плоскости. Чтобы сканер правильно понял введенную для построения функцию, при её вводе нужно пользоваться правилами (рисунок 1) Для расчета значения функции в конкретной точке необходимо задать значение х и нажать кнопку вычислить. 18 http://antibotan.com/ - Всеукраїнський студентський архів Рисунок 1 – Правила задания функций ПРИМЕР РАБОТЫ ПРОГРАММЫ 19 http://antibotan.com/ - Всеукраїнський студентський архів Рисунок 3 – Иллюстрация выявления ошибок ЗАКЛЮЧЕНИЕ 20 http://antibotan.com/ - Всеукраїнський студентський архів Для написания данного приложения использовался метод объектноориентированного программирования, что упростило написание программы, также активно использовались динамические и рекурсивные структуры данных. Функции анализа и интерпретирования введенной строки хранятся в DLL библиотеке (DLL_Calc.dll) 21 http://antibotan.com/ - Всеукраїнський студентський архів БИБЛИОГРАФИЧЕСКИЙ СПИСОК 1. Шилдт Г. Самоучитель С++; Пер. с англ. – 3-е изд. – СПб.: БХВ Петербург, 2009. 2. Макконелл С. Совершенный код. Мастер-класс; Пер. с англ. – СПб.: Питер, 2005. 3. Хортон Айвор. Visual C++ 2008: базовый курс.: Пер. с англ. – М.: «И,Д, Вильямс», 2009. Также использовались материалы Интернет-ресурсов: http://www.cyberguru.ru/ http://msdn.microsoft.com/ru-ru/default.aspx http://algolist.manual.ru/ 22