Пример 1. Простые вычисления с помощью класса MathParser
Мы начнем с примера, разъясняющего работу с классом MathParser. В приложении, показанном на рисунке 1, пользователь вводит выражение, зависящее от значения переменной x, и численное значение этой переменной, при этом допустим такой синтаксис, как sqrt(2*pi) или e^2. Затем, для вычисления значения выражения, пользователь нажимает клавишу ввода.
Заметьте, что для вычисляемых выражений класс MathParser использует математические функции и константы языка ActionScript. Подобно Java, JavaScript, C++, и многим другим языкам программирования, математические функции ActionScript и константы поддерживают двойную степень точности (не уверен правильно ли я перевел термин double precision accuracy). Важно учитывать данный факт, т.к. это может привести к странным (неожиданным) результатам. Например, если вы попробуете вычислить значение выражения tan(pi/2), то (т.к. значение константы pi/2 не может быть выражено в численном виде абсолютно точно) вы получите значение тангенса при значении переменной x очень и очень близком к pi/2 (но не равном ему), а это очень большое число (хотя как мы знаем, что функция тангенса не определена при pi/2). Мораль здесь в том, что, чтобы создать точный научный калькулятор, нужно проделать намного больше работы, чем просто суметь вычислить значения встроенных функций и констант языка программирования.
Настройка сцены
Чтобы создать приложение, изображенное на рис. 1, сначала нам понадобится несколько элементов на сцене. Это должны быть 2 текстовых поля для ввода текста с именами inVal и inExp и динамическое текстовое поле txtResult. Остальную работу сделает скрипт.
Рис. 1. Простенький научный калькулятор (для просмотра щелкните по изображению) |
Пишем код
Сначала мы импортируем класс MathParser, а также другие необходимые классы. Отметим, что папка flashandmath должна находиться в той же папке, что и исходный файл приложения.
package { import flashandmath.as3.parsers.*; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFieldType; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.ui.Keyboard; import teormech.utils.TextUtils; public class Main extends Sprite { ...
var mpExp:MathParser = new MathParser(["x"]); var mpVal:MathParser = new MathParser([ ]);
var compobjExp:CompiledObject = new CompiledObject(); var compobjVal:CompiledObject = new CompiledObject();
// Данная строка строка взята из тела функции init() stage.addEventListener( KeyboardEvent.KEY_DOWN, compute); ... private function compute(kevt:KeyboardEvent):void { if (kevt.keyCode != Keyboard.ENTER) { return; } // «Захват» содержимого текстовых полей var stVal:String = inVal.text; var stExpression:String = inExp.text; // «Компиляция» выражений, введенных пользователем compobjVal = mpVal.doCompile(stVal); compobjExp = mpExp.doCompile(stExpression); // Проверка на наличие синтаксических ошибок // во введенных выражениях. // При возникновении ошибки в соответствующем // текстовом поле выводится сообщение if (compobjVal.errorStatus == 1) { txtResult.text = compobjVal.errorMes; stage.focus = inVal; return; } if (compobjExp.errorStatus == 1) { txtResult.text = compobjExp.errorMes; stage.focus = inExp; return; } // Вычисление значения выражения переменной var xVal:Number = mpVal.doEval( compobjVal.PolishArray, [ ]); // Вычисление значение выражения, // зависящего от значения переменной var resVal:Number = mpExp.doEval( compobjExp.PolishArray, [xVal]); // Вывод ответа с точностью то десятых долей. txtResult.text = resVal.toFixed(10); }
Так как выражение для анализатора mpVal не содержит переменных, то ему мы передали пустой массив, а для анализатора mpExp мы передали массив с одним элементом (содержащим значение этой переменной), т.к. это выражение содержит одну переменную.
Исходники к этому примеру можно взять здесь.
Пример 2. Использование класса MathParser для создания простой математической викторины
У класса MathParser может быть множество вариантов применения. Здесь мы рассмотрим простой пример, в котором осуществляется проверка ответа студента на вопросы с произвольно генерируемыми исходными данными (см. рис.2).
Рис. 2. Проверьте свои знания по школьной математике (для проверки щелкните по картинке) |
Идея, лежащая в основе проверки вводимого ответа, такова: программа берет numpoints значений переменной (в данном случае переменной x), лежащих в промежутке между значениями min и max, и сравнивает результаты вычисления «правильного» выражения (которое выводит сама программа) с результатами вычисления выражения, введенного пользователем при выбранных значениях переменной. Если все результаты вычислений отличаются не более чем на величину tolerance, то ответ считается правильным. Хотя это и ненадежный механизм для проверки алгебраических ответов, он достаточно хорошо работает в данных условиях, когда мы знаем, что ответы являются простыми многочленами. Устанавливая значения переменных numpoints, min, max, и tolerance в начале программы, мы можем более точно ее настраивать для каждого конкретного случая.
Настройки сцены
Чтобы создать описываемое приложение, Вам понадобится расположить на сцене несколько объектов: это ярлыки (labels), динамические текстовые поля (txtInitial, txtRecursivePart, txtResponseBox) и текстовое поле для ввода текста пользовательского ответа txtUserAnswer, а также кнопки btnCheck и btnNext.
Пишем код
Сперва мы импортируем необходимые классы (MathParser в их числе) и далее задаем значения переменных-параметров нашей программы, предназначенных для проверки ответа:
package { import com.bit101.components.Label; import com.bit101.components.PushButton; import com.bit101.components.Style; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFieldType; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.ui.Keyboard; import teormech.utils.TextUtils; import flashandmath.as3.parsers.*; public class Main extends Sprite { private var txtPts:TextField; private var txtUserAnswer:TextField; private var txtResponseBox:TextField; private var btnCheck:PushButton; private var btnNext:PushButton; private var numpoints:Number = 12; private var tolerance:Number = 0.0000001; private var min:Number = 2; private var max:Number = 5; private var stPt1:String; private var stPt2:String; private var stUserAnswer:String; private var stCorrectAnswer:String; ...
private function nextProblem():void { var a:int; var b:int; var x1:int; var y1:int; var x2:int; var y2:int; /* Выбираем случайные значения параметров для задачи, лежащие в диапазоне {-2; 5} */ a = 5 - Math.floor(8*Math.random()); b = 5 - Math.floor(8*Math.random()); x1 = 5 - Math.floor(8*Math.random()); x2 = 5 - Math.floor(8*Math.random()); if (x1 == x2) { x2 = x1 + 1; } y1 = a*x1 + b; y2 = a*x2 + b; stCorrectAnswer = String(a) + "*x + " + String(b); txtPts.text = "(" + String(x1) + ", " + String(y1) + ") and (" + String(x2) + ", " + String(y2) + ")."; txtResponseBox.text = ""; txtUserAnswer.text = ""; }
private function checkAnswer():void { // Проверка – был ли введен ответ. Если нет, // то ничего не делаем var stUserAnswer:String = txtUserAnswer.text; if (stUserAnswer.length == 0) { return; } // Осуществляем анализ введенного пользователем // и правильного выражений var procFun:MathParser = new MathParser(["x"]); var corrCO:CompiledObject= procFun.doCompile(stCorrectAnswer); var userCO:CompiledObject = procFun.doCompile(stUserAnswer); // Сообщаем об ошибке, если какое-либо из выражений // содержит синтаксическую ошибку if (userCO.errorStatus == 1) { txtResponseBox.text = "Ошибка синтаксиса в пользовательском ответе. " + userCO.errorMes; return; } if (corrCO.errorStatus == 1) { txtResponseBox.text = "Ошибка синтаксиса в программном ответе. " + corrCO.errorMes; return; } /* Выбираем значения переменной x и интервала [min; max] и находим значения y для выражений введенных пользователем и правильного ответа. Если ответы различаются более, чем на величину переменной tolerance, то мы знаем, что ответ неправильный. */ var thisX:Number; var userY:Number; var corrY:Number; var i:Number; for (i = 0; i < numpoints; i++) { thisX = min + i*(max - min)/numpoints; userY = procFun.doEval(userCO.PolishArray,[thisX]); corrY = procFun.doEval(corrCO.PolishArray,[thisX]); if (Math.abs(userY - corrY) > tolerance) { var incorrectTextFormat:TextFormat = new TextFormat("Arial", "18", 0xff0000); txtResponseBox.text = "Неправильно"; txtResponseBox.setTextFormat(incorrectTextFormat); return; } } // Если же мы не нашли ошибки, то ответ правильный txtResponseBox.text = "Правильно!"; txtResponseBox.setTextFormat(txtResponseBox.defaultTextFormat); }
btnCheck.addEventListener( MouseEvent.CLICK, btnCheckPressed); stage.addEventListener( KeyboardEvent.KEY_DOWN, keyPressed); ... private function btnCheckPressed(e:MouseEvent):void { checkAnswer(); } function keyPressed(evt:KeyboardEvent):void { // Проверка ответа срабатывает только при нажатии // на ENTER if (evt.keyCode == Keyboard.ENTER) { checkAnswer(); } }
nextProblem();
UPD: В исходных классах пакета flashandmath у многих переменных отсутствует типизация, если работать во Flash IDE, то никаких проблем не возникает, но если вы разрабатываете приложения с помощью Flex SDK, то компилятор ругается на отсутствие типизации переменных и идентификаторов, поэтому мне пришлось потратить немного времени для устранения этой проблемки. Классы пакета flashandmath, включенные в исходники к примерам отличаются от официальных лишь добавлением типизации.
Комментариев нет:
Отправить комментарий