Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Операции над рациональными числами




Определим над рациональными числами стандартный набор операций - сложение и вычитание, умножение и деление. Реализуем эти операции методами с именами Plus, Minus, Mult, Divide соответственно. Поскольку рациональные числа - это прежде всего именно числа, то для выполнения операций над ними часто удобнее пользоваться привычными знаками операций (+, -, *, /). Язык C# допускает определение операций, заданных указанными символами. Этот процесс называется перегрузкой операций, и мы рассмотрим сейчас, как это делается. Конечно, можно было бы обойтись только перегруженными операциями, но мы приведем оба способа. Пользователь сам будет решать, какой из способов применять в конкретной ситуации - вызывать метод или операцию.

Покажем вначале реализацию метода Plus и операции +:

public Rational Plus(Rational a){ int u,v; u = m*a.n +n*a.m; v= n*a.n; return(new Rational(u, v));}//Pluspublic static Rational operator +(Rational r1, Rational r2){ return (r1.Plus(r2));}

Метод Plus реализуется просто. По правилам сложения дробей вычисляется числитель и знаменатель результата, и эти данные становятся аргументами конструктора, создающего требуемое рациональное число, которое удовлетворяет правилам класса.

Обратите внимание на то, как определяется операция класса. Именем соответствующего метода является сам знак операции, которому предшествует ключевое слово operator. Важно также помнить, что операция является статическим методом класса с атрибутом static.

В данном конкретном случае операция реализуется вызовом метода Plus. Как теперь все это работает? Вот пример:

public void TestPlusRational(){ Rational r1=new Rational(0,0), r2 = new Rational(1,1); Rational r3=new Rational(10,8), r4 = new Rational(2,6); Rational r5=new Rational(4,-12), r6 = new Rational (-12,-14); Rational r7,r8, r9,r10, r11, r12; r7 = r1.Plus(r2); r8 = r3.Plus(r4); r9 = r5.Plus(r6); r10 = r1+r2; r11 = r3+r4; r12 = r5+r6+r10+r11; r1.PrintRational("r1:(0,0)"); r2.PrintRational("r2:(1,1)"); r3.PrintRational("r3:(10,8)"); r4.PrintRational("r4:(2,6)"); r5.PrintRational("r5: (4,-12)"); r6.PrintRational ("r6: (-12,-14)"); r7.PrintRational("r7: (r1+r2)"); r8.PrintRational ("r8: (r3+r4)"); r9.PrintRational("r9: (r5+r6)"); r10.PrintRational ("r10: (r1+r2)"); r11.PrintRational("r11: (r3+r4)"); r12.PrintRational("r12: (r5+r6+r10+r11)");}

Обратите внимание на вычисление r12: здесь ощутимо видно преимущество операций, позволяющих записывать сложные выражения в простой форме. Аналогичным образом определим остальные операции над рациональными числами:

public Rational Minus(Rational a){ int u,v; u = m*a.n - n*a.m; v= n*a.n; return(new Rational(u, v));}//Minuspublic static Rational operator -(Rational r1, Rational r2){ return (r1.Minus(r2));}public Rational Mult(Rational a){ int u,v; u = m*a.m; v= n*a.n; return(new Rational(u, v));}//Multpublic static Rational operator *(Rational r1, Rational r2){ return (r1.Mult(r2));}public Rational Divide(Rational a){ int u,v; u = m*a.n; v= n*a.m; return(new Rational(u, v));}//Dividepublic static Rational operator /(Rational r1, Rational r2){ return (r1.Divide(r2));}

Вот тест, проверяющий работу этих операций:

public void TestOperRational(){ Rational r1=new Rational(1,2), r2 = new Rational(1,3); Rational r3, r4, r5, r6; r3 = r1- r2; r4 = r1*r2; r5 = r1/r2; r6 = r3+r4*r5; r1.PrintRational("r1: (1,2)"); r2.PrintRational("r2: (1,3)"); r3.PrintRational("r3: (r1-r2)"); r4.PrintRational("r4: (r1*r2)"); r5.PrintRational("r5: (r1/r2)"); r6.PrintRational("r6: (r3+r4*r5)");}

Обратите внимание: при перегрузке операций сохраняется общепринятый приоритет операций. Поэтому при вычислении выражения r3+r4*r5 вначале будет выполняться умножение рациональных чисел, а потом уже сложение.

Константы класса Rational

Рассмотрим важную проблему определения констант в собственном классе. Определим две константы 0 и 1 класса Rational. Кажется, что сделать это невозможно из-за ограничений, накладываемых на объявление констант. Напомню, константы должны быть инициализированы в момент объявления, и их значения должны быть заданы константными выражениями, известными в момент компиляции. Но в момент компиляции у класса Rational нет никаких известных константных выражений. Как же быть? Справиться с проблемой поможет статический конструктор, созданный для решения подобных задач. Роль констант класса будут играть статические поля, объявленные с атрибутом readonly, то есть доступные только для чтения. Нам также будет полезен закрытый конструктор класса. Еще укажем, что введение констант класса требует использования экзотических средств языка C#. Вначале определим закрытый конструктор:

private Rational(int a, int b, string t){ m = a; n = b;}

Не забудем, что при перегрузке методов (в данном случае конструкторов) сигнатуры должны различаться, и поэтому пришлось ввести дополнительный аргумент t для избежания конфликтов. Поскольку конструктор закрытый, то гарантируется корректное задание аргументов при его вызове. Определим теперь константы класса, которые, как я уже говорил, задаются статическими полями с атрибутом readonly:

//Константы класса 0 и 1 - Zero и One public static readonly Rational Zero, One;

А теперь зададим статический конструктор, в котором определяются значения констант:

static Rational(){ Console.WriteLine("static constructor Rational"); Zero = new Rational(0, 1, "private"); One = new Rational (1, 1, "private");}//Статический конструктор

Как это все работает? Статический конструктор вызывается автоматически один раз до начала работы с объектами класса. Он и задаст значения статических полей Zero, One, представляющих рациональные числа с заданным значением. Поскольку эти поля имеют атрибут static и readonly, то они доступны для всех объектов класса и не изменяются в ходе вычислений, являясь настоящими константами класса. Прежде чем привести пример работы с константами, давайте добавим в наш класс важные булевы операции над рациональными числами - равенство и неравенство, больше и меньше. При этом две последние операции сделаем перегруженными, позволяя сравнивать рациональные числа с числами типа double:

public static bool operator ==(Rational r1, Rational r2){ return((r1.m ==r2.m)&& (r1.n ==r2.n));} public static bool operator!=(Rational r1, Rational r2){ return((r1.m!=r2.m)|| (r1.n!=r2.n));} public static bool operator <(Rational r1, Rational r2){ return(r1.m * r2.n < r2.m* r1.n);} public static bool operator >(Rational r1, Rational r2){ return(r1.m * r2.n > r2.m* r1.n);}public static bool operator <(Rational r1, double r2){ return((double)r1.m / (double)r1.n < r2);}public static bool operator >(Rational r1, double r2){ return((double)r1.m / (double)r1.n > r2);}

Наш последний пример демонстрирует работу с константами, булевыми и арифметическими выражениями над рациональными числами:

public void TestRationalConst(){ Rational r1 = new Rational(2,8), r2 =new Rational(2,5); Rational r3 = new Rational(4, 10), r4 = new Rational(3,7); Rational r5 = Rational.Zero, r6 = Rational.Zero; if ((r1!= Rational.Zero) && (r2 == r3))r5 = (r3+Rational.One)*r4; r6 = Rational.One + Rational.One; r1.PrintRational("r1: (2,8)"); r2.PrintRational ("r2: (2,5)"); r3.PrintRational("r3: (4,10)"); r4.PrintRational("r4: (3,7)"); r5.PrintRational("r5: ((r3 +1)*r4)"); r6.PrintRational("r6: (1+1)");}

Финальный проект

В финальном проекте создается семейство классов, описывающих геометрические фигуры. Проектирование начинается с абстрактного класса поведения, который описывает общие свойства и методы, характерные для всех фигур семейства. Затем, используя наследование, создаются классы конкретных геометрических фигур, начиная с простейших, таких, как круги и прямоугольники, до составных, таких, как класс Person. Мы добавим в наш проект динамические структуры данных, такие, как список с курсором, для хранения в нем фигур семейства. Наконец, мы создадим интерфейс, включающий меню с десятками команд и панель с инструментальными кнопками. Интерфейс позволяет конечному пользователю выполнять различные действия над геометрическими фигурами - создавать, рисовать их на форме, перемещать их с помощью команд и перетаскивать их мышью, менять их размеры и цвет, сохранять в списке и удалять из списка, отображать все фигуры списка или очистить его полностью.

Проект может служить образцом полноценного Windows-приложения, примером проектирования в классах с демонстрацией преимуществ, предоставляемых наследованием. Закончим на этом рекламную часть и приступим к делу. Хочу предупредить, вас ждут программные тексты, почти без всяких комментариев. Все нужные комментарии были даны в предыдущих лекциях. С моей точки зрения, наиболее интересная часть программистских книжек - это та, в которой приводится программный код. И значит, эта глава самая интересная.

Абстрактный класс Figure

Приведем код класса:

using System;using System.Drawing;namespace Shapes{/* <summary>Figure - это абстрактный класс; прародитель семейства классов геометрических фигур. Все фигуры имеют: центр - center, масштаб - scale. Статус перетаскивания - dragged center - объект встроенного класса (структуры) Point. Этот объект задает характерную точку фигуры - чаще всего ее центр (тяжести), scale задает масштаб фигуры, первоначально единичный. drugged = true, когда фигура следует за курсором мыши. Над фигурами определены операции: параллельный перенос - Move(a,b) масштабирование - Scale(s) Показ фигуры - Show. Область захвата - Region_Capture возвращает прямоугольник, характерный для фигуры, перетаскивание фигуры возможно при установке курсора мыши в области захвата. </summary> */abstract public class Figure{ /// <summary> /// закрытые для клиентов атрибуты класса - center, scale /// </summary> protected Point center; protected double scale; protected bool dragged; protected Color color; //Доступ к свойствам public Point center_figure { get {return(center);} set {center = value;} } public double scale_figure { get {return(scale);} set {scale = value;} } public bool dragged_figure { get {return(dragged);} set {dragged = value;} } public Color color_figure { get {return(color);} set {color = value;} } /* <summary> базовый конструктор фигур </summary> <param name="x">координата X характерной точки фигуры</param> <param name="y">Координата Y характерной точки фигуры</param> public Figure(int x, int y) { center = new Point(x,y); scale = 1; dragged = false; color = Color.ForestGreen; } /* <summary> отложенный метод Параллельный перенос фигуры на (a,b) require: true; ensure: для любой точки фигуры p(x,y): x = old(x) +a; y = old(y) + b; </summary> */ /*<param name="a"> a - перемещение по горизонтали вправо </param> <param name="b"> b - перемещение по вертикали вниз</param> Замечание: Для того, чтобы фигура при рисовании была полностью видимой, координаты всех ее точек должны быть в пределах области рисования. */ public void Move (int a,int b) { center.X +=a; center.Y += b; } /* <summary> изменяет масштаб фигуры </summary> <param name="s">масштаб изменяется в s раз</param> */ public void Scale(double s) { scale*=s; } /* <summary> рисование фигуры в окне, передающем объекты g и pen </summary> <param name="g"> графический объект, методы которого рисуют фигуру</param> <param name="pen">перо рисования</param> */ public abstract void Show(Graphics g, Pen pen, Brush brush); public abstract System.Drawing.Rectangle Region_Capture(); }}

Абстрактный класс, относящийся к этапу проектирования системы, вместе с тем является важнейшим элементом заключительного семейства классов. В этом проявляется мощь объектно-ориентированного подхода к разработке программных систем. Заметьте, на данном уровне большая часть текста представляет документацию, являющуюся неотъемлемой частью программного проекта. Документация записана в тегах <summary>, что позволяет автоматически ее извлечь и сохранить в виде XML-отчета.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...