Практика 8
Введение в абстракцию данных
Абстракция данных - это методология, которая позволяет отделить способ использования составного объекта данных от деталей того, как он составлен из элементарных данных. [SICP 2.1] То есть используя данные, наши программы не должны делать о них никаких предположений, кроме абсолютно необходимых для выполнения поставленной задачи. В тоже время конкретное представление данных определяется независимо от программ, которые эти данные используют.
В книге SICP интерфейсом между двумя частями системы служит набор процедур, называемых селекторами и конструкторами, реализующих абстрактные данные в терминах конкретного представления. Мы тоже начнем с такого подхода.
Задания в классе
Есть простые задания, средние и сложные. За каждое из них можно получить разное количество баллов. Можно не тратить время на простое задание и делать сложное, чтобы получить много баллов и шанс на автомат.
Если вы умеете, делайте всё на классах. Переопределяйте магические методы, чтобы переопределить операторы(суммы, сравнение и прочее). Выбрасывайте исключения, если надо(деление на 0). В IDEA можете нажать “generate” -> “override methods” и она покажет вам список всех магических методов, которые вам надо будет переопределить.
Как делать классы можно прочитать тут или в официальной документации. Вам, возможно, будет интересно как переопределить операторы в Python, тогда вам сюда.
Рациональные числа. Easy.
Требуется создать абстракцию рациональных чисел с возможностью: создавать, складывать, вычитать, умножать, делить, сравнивать, печатать и превращать их в float number. Результаты должны быть всегда с наименьшим знаменателем (алгоритм Евклида в помощь). Целую часть выделять не надо.
Итого, нужен тип данных, который реализует следующие правила:
Hints
Вам скорее всего понадобится создать функцию make_rat(n1, d1)
, которая вернёт нечто. Например, словарь методов, какой-то объект или еще что-то. В качестве базового типа предлагается использовать tuple
. Если вы умеете, можете сделать всё на основе классов.
Тесты
Вы можете использовать эти тесты, если работает с классами. Вам нужно будет переопределить магические методы, чтобы создать свои собственные операторы. Например, __add__
для сложения.
import unittest
class Fraction:
pass
class TestFraction(unittest.TestCase):
def testEg(self):
self.assertTrue(Fraction(1, 2) == Fraction(-3, -6))
self.assertTrue(Fraction(9, 15) == Fraction(6, 10))
self.assertTrue(Fraction(1, 3) == Fraction(1, 3))
def testLt(self):
self.assertTrue(Fraction(1, 2) < Fraction(2, 3))
self.assertTrue(Fraction(-1, 2) < Fraction(1, 2))
self.assertTrue(Fraction(0, 2) < Fraction(2, 3))
def testGt(self):
self.assertTrue(Fraction(2, 3) > Fraction(1, 3))
def testGe(self):
self.assertTrue(Fraction(2, 3) >= Fraction(1, 3))
self.assertTrue(Fraction(2, 3) >= Fraction(4, 6))
def testLe(self):
self.assertTrue(Fraction(1, 2) <= Fraction(2, 3))
self.assertTrue(Fraction(2, 3) <= Fraction(2, 3))
def testFloat(self):
self.assertEqual(float(Fraction(1, 2)),0.5)
self.assertEqual(float(Fraction(-1, 5)), -0.2)
self.assertEqual(float(Fraction(10, 2)), 5)
self.assertEqual(float(Fraction(3, -8)), -0.375)
def testADD(self):
self.assertEqual(Fraction(1, 2) + Fraction(1, 3), Fraction(5, 6))
self.assertEqual(Fraction(1, 2) + Fraction(1, 6), Fraction(2, 3))
self.assertEqual(Fraction(1, 2) + Fraction(1, -3), Fraction(1, 6))
def testSUB(self):
self.assertEqual(Fraction(1, 2) - Fraction(1, 3), Fraction(1, 6))
self.assertEqual(Fraction(1, 6) + Fraction(1, 2), Fraction(-1, 3))
def testSUB(self):
self.assertEqual(Fraction(1, 2) - Fraction(1, 3), Fraction(1, 6))
self.assertEqual(Fraction(1, 6) - Fraction(1, 2), Fraction(-1, 3))
self.assertEqual(Fraction(1, 3) - Fraction(1, 3), Fraction(0, 3))
def testMUL(self):
self.assertEqual(Fraction(1, 2) * Fraction(1, 3), Fraction(1, 6))
self.assertEqual(Fraction(1, 2) * Fraction(2, 1), Fraction(1, 1))
self.assertEqual(Fraction(1, 2) * Fraction(-1, 2), Fraction(-1, 4))
self.assertEqual(Fraction(-1, 2) * Fraction(1, -2), Fraction(1, 4))
def testDIV(self):
self.assertEqual(Fraction(1, 2) / Fraction(1, 2), Fraction(1, 1))
self.assertEqual(Fraction(1, 2) / Fraction(1, 3), Fraction(3, 2))
def testZiro(self):
try:
Fraction(2, 0)
self.assertTrue(False)
except(ArithmeticError):
pass
if __name__ == '__main__':
unittest.main()
Интервальная арифметика. Moderate.
Идея появилась из проблем инженеров. У них принято характеристики физических объектов описывать в рамках некоторых интервалов. Например, сопротивление резистора бывает 6.8 Ом с погрешностью 10%, что значит сопротивление лежит в интервале [6.12;7.48] Ом
.
Необходимо разработать абстрактную структуру данных, на которой возможны все арифметические операции: сложение, вычитание, умножение, деление. И дополнительные функции: создание, сравнение(проверка равенства), форматирование в красивой форме, вычисление радиуса погрешности (в примере это 0.68).
Например: резистор R1=[6.8;10%]Ом
подключен параллельно к резистору R2=[4.7;5%]Ом
, тогда сопротивление их комбинации будет R3=[2.58;2.97]Ом
.
Функции создания должны быть такие: прямое задание интервала по верхней и нижней точке, задание с процентом погрешности, задание с радиусом погрешности.
def buildIntervalWithPercentage(center, percent):
pass
def buildIntervalWithLowerUpper(lower, upper):
pass
def buildIntervalWithRadius(center, radius):
pass
Тесты
Придумайте сами исчерпывающий набор тестов python unittest.
Hints
Базовая структура данных скорее всего будет tuple
.
Можете вдохновляться этой библиотекой interval-arithmetic
Гуглить книгу КОНЕЧНОМЕРНЫЙ ИНТЕРВАЛЬНЫЙ АНАЛИЗ С. П. Шарый.
Статья в вики на английском Interval_arithmetic.
Вопросы
Хитрый вопрос: что будет, если интервал пересекает 0?
Очень хитрый вопрос: проверьте, действительно ли вы получите одинаковый результат для следующей формулы:
Ультрахитрый вопрос: а можете сделать, чтобы получилось равенство?
Домашнее задание
Сделать как можно больше заданий.
За помощью можете обратиться к книге “Структура и интерпретация компьютерных программ” и главе 2.