Programming Taskbook


E-mail:

Пароль:

Регистрация пользователя   Восстановление пароля

 

ЮФУ SMBU

Электронный задачник по программированию

©  М. Э. Абрамян (Южный федеральный университет, Университет МГУ-ППИ в Шэньчжэне), 1998–2025

 

PT for OOP | Группы заданий | OOP0Begin

PrevNext


Введение в ООП

Базовые принципы ООП

OOP0Begin1°. Поля и методы класса. Инкапсуляция.

Реализовать класс Stack, моделирующий стек целых чисел. Данные стека хранятся в виде односвязной цепочки узлов, имеющих два поля: data (содержит целое число — значение элемента стека) и next (содержит ссылку на следующий элемент стека). Узел реализовать в виде вспомогательного класса StackNode с открытыми полями data и next. Поля должны инициализироваться в конструкторе с помощью его одноименных параметров. Класс Stack включает закрытое поле top типа StackNode, содержащее ссылку на вершину стека (или пустую ссылку, если стек пуст), конструктор без параметров, создающий пустой стек, и набор открытых методов:

• Empty (без параметров) — возвращает True, если стек пуст, и False в противном случае;

• Push(data) — добавляет в стек элемент со значением data (целочисленный параметр), ничего не возвращает;

• Pop (без параметров) — извлекает из стека верхний элемент и возвращает его значение (если стек пуст, то возвращает число 0);

• Peek (без параметров) — возвращает значение верхнего элемента, не удаляя его из стека (если стек пуст, то возвращает число 0);

• Clear (без параметров) — удаляет из стека все элементы, ничего не возвращает;

• ToStr (без параметров) — возвращает строковое представление содержимого стека в формате «v1-v2-…-vN-end», где v1, v2, …, vN — значения элементов стека, начиная с его вершины (например, «12–34–end»); для пустого стека функция должна возвращать только слово «end».

Обычно внешняя программа может использовать только методы класса, которые объявляются открытыми (public), в то время как его поля делаются закрытыми (private). Это гарантирует, что внешняя программа не сможет изменить поля неправильным образом и тем самым нарушить нормальную работу объекта. Кроме того, скрытие полей дает возможность в дальнейшем изменить реализацию класса (определяемую его полями), не изменяя его интерфейс (определяемый методами и используемый внешними программами). Так обеспечивается первый принцип ООП: принцип инкапсуляции (сокрытия данных).

Тестирование разработанной системы классов. Дана строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека. Для команд Empty, Pop, Peek, Clear элемент данных представляет собой строку, совпадающую с именем команды; для команды Push элемент данных включает строку «Push» и целое число, которое требуется добавить в стек; команда ToStr в набор не входит.

Создать объект s типа Stack и выполнить для него все команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name. Все команды для стека должны вызываться во вспомогательной функции StackTest с параметром-ссылкой типа Stack.

Информация о каждой команде занимает одну строку файла. Вначале выводится имя команды, двоеточие и пробел. Затем, если команда возвращает целочисленное значение, то выводится это значение, а если команда возвращает логическое значение, то выводится строка «True» в случае значения True и строка «False» в случае значения False. Кроме того, для каждой команды выводится строковое представление стека; для этого после вызова каждой команды дополнительно вызывается метод ToStr для объекта s, и его возвращаемое значение записывается в файл на той же строке, что и предыдущие данные текущей команды, отделяясь от них одним пробелом.

Примечание (C++). Члены класса по умолчанию являются закрытыми (перед ними можно указывать модификатор private:, но это не является обязательным). Чтобы сделать член класса открытым, надо указать перед ним модификатор public:. Модификатор protected: разрешает доступ к члену класса для любых производных классов (если наследование не является закрытым). Любой из этих модификаторов действует до появления нового модификатора.

[C++]

class StackNode
{
public:
    int data;
    shared_ptr<StackNode> next;
    StackNode(int data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
    ~StackNode()
    {
        Show("StackNode:", data);
    }
};

class Stack
{
    shared_ptr<StackNode> top = nullptr;
public:
    void Push(int data)
    {
        top = make_shared<StackNode>(data, top);
    }
    // Implement other methods of the Stack class

};

void StackTest(shared_ptr<Stack> s)
{
    // Implement the StackTest function
}

Примечание (C#). При разработке классов, которые должны использоваться в различных приложениях, их надо снабжать модификатором public. Если класс предназначен только для использования в пределах текущей сборки (как правило, динамической библиотеки), он должен иметь модификатор internal (который можно не указывать); это не позволит другим программам использовать данный класс.

Члены класса по умолчанию являются закрытыми (для них можно указывать модификатор private, но это не является обязательным). Чтобы сделать член класса открытым, надо указать модификатор public. Другие модификаторы доступа для членов класса: internal (член доступен только в пределах текущей сборки) и protected (член доступен для любых производных классов).

[C#]

internal class StackNode
{
    internal int data;
    internal StackNode next;
    internal StackNode(int data, StackNode next)
    {
        this.data = data;
        this.next = next;
    }
}

public class Stack
{
    private StackNode top = null;
    public void Push(int data)
    {
        top = new StackNode(data, top);
    }
    // Implement other methods of the Stack class

}

static void StackTest(Stack s)
{
    // Implement the StackTest function

}

Примечание (Java). При разработке классов, которые должны использоваться в различных приложениях, их надо снабжать модификатором public. Один файл может иметь только один public-класс, и имя файла должно совпадать с именем этого класса.

При описании закрытых членов класса надо использовать модификатор private. Чтобы сделать член класса открытым, надо указать модификатор public. Модификатор protected разрешает доступ к члену класса для любых производных классов и для любых классов того же пакета. Отсутствие модификатора делает возможным доступ к члену класса только в пределах пакета.

[Java]

class StackNode {
    public int data;
    public StackNode next;

    public StackNode(int data, StackNode next) {
        this.data = data;
        this.next = next;
    }
}

class Stack {
    private StackNode top = null;

    public void push(int data) {
        top = new StackNode(data, top);
    }

    // Implement other methods of the Stack class

}

public class MyTask extends PT
{
    public static void stackTest(Stack s) throws IOException {
        // Implement the stackTest function
    }

Примечание (Python). В языке Python поля и методы класса по умолчанию являются открытыми. Если какие-либо поля надо сделать закрытыми, то их имена надо начинать с двух символов подчеркивания. Методы класса тоже можно сделать закрытыми с помощью добавления двух символов подчеркивания, однако это не относится к специальным методам, который начинаются и заканчиваются двойным подчеркиванием; примером является метод __init__. Метод __init__ в языке Python играет роль конструктора (в нем определяются поля класса и выполняются инициализирующие действия), поскольку «настоящий» конструктор __new__ обычно не переопределяется. При определении метода в классе его первый параметр всегда соответствует тому объекту, для которого вызывается метод. Обычно для этого параметра используется имя self.

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data, self.__top)

    # Implement other methods of the Stack class

def stackTest(s):
    pass
    # Implement the stackTest function

Примечание 1 (Ruby). В Ruby имена полей классов начинаются с символа @, и по умолчанию они закрыты. Если требуется обеспечить доступ к какому-либо полю f из внешней программы, то при определении класса можно использовать конструкцию attr_reader :f (для доступа только на чтение) или attr_accessor :f (для доступа на чтение и запись). Методы класса по умолчанию открыты (за исключением некоторых особых методов, например, initialize). Метод initialize в языке Ruby играет роль конструктора (в нем определяются поля класса и выполняются инициализирующие действия), поскольку «настоящий» конструктор new обычно не переопределяется. Если при определении методов класса указать слова private, public или protected, то методы, указанные после этих слов, будут иметь соответствующий уровень доступа.

Следует иметь в виду, что в языке Ruby уровни доступа private и protected имеют другой смысл, чем в большинстве других объектно-ориентированных языков. Уровень доступа private означает, что метод можно вызвать в другом методе, причем как этого класса, так и производных от него классов, однако этот метод нельзя вызвать для других экземпляров класса, даже если эти экземпляры используются в методе того же класса, в котором определен private-метод. Уровень доступа protected снимает это ограничение: protected-методы можно вызывать для любых экземпляров класса, которые используются в методах исходного и производных классов.

Примечание 2 (Ruby). В Ruby нельзя использовать идентификатор next в качестве имени параметра метода, так как он является служебным словом (аналог continue в других языках программирования), поэтому в конструкторе initialize класса StackNode для второго параметра необходимо использовать другой идентификатор (например, nextNode). В качестве имени поля класса идентификатор next использовать можно.

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data, @top)
    end

    # Implement other methods of the Stack class

end

def stackTest(s)
    # Implement the stackTest function
end

Примечание 1 (Julia). В отличие от других языков, в Julia нет классов, поэтому вместо классов необходимо использовать структуры (struct). Кроме того, Julia не поддерживает инкапсуляцию функций-членов, как другие объектно-ориентированные языки; только одна функция может быть помещена внутрь составного типа: внутренний конструктор, в котором объект создается с помощью операции new (обычно внутренние конструкторы используются, чтобы избежать бесконечной рекурсии). Например, если структура S имеет единственное строковое поле и требуется, чтобы это строковое поле содержало только строчные (маленькие) буквы, то определение обычного конструктора вида S(info::String) = S(lowercase(info)) приведет к бесконечной рекурсии. Для решения этой проблемы конструктор помещается в определение структуры и определяется с помощью операции new: S(info::String) = new(lowercase(info)). Также существуют конструкторы по умолчанию: если в конструкторе достаточно присвоить значения параметров соответствующим полям, то такой конструктор не требуется определять явным образом. Вместо определения функций-членов необходимо использовать обычные функции, передавая им в качестве первого параметра объект соответствующего типа.

Примечание 2 (Julia). В Julia также отсутствует возможность инкапсуляции полей: все поля любой структуры являются доступными; поэтому можно обеспечить лишь условное скрытие данных, используя особые имена для соответствующих полей (например, добавляя к ним символы подчеркивания) и включая в документацию к этой структуре указание не обращаться явным образом к подобным полям.

Примечание 3 (Julia). Подобно Python, в Julia используется механизм динамической типизации, то есть программа может успешно работать даже если тип переменной не указан (и определяется во время выполнения). Однако подобный подход в Julia может приводить к большим потерям эффективности или даже к ошибкам времени выполнения. Поэтому рекомендуется всегда снабжать переменные явными описаниями и избегать ситуаций, которые приводят к изменению типа переменной во время выполнения программы. Имя типа указывается после имени переменной (в частности, поля структуры) и отделяется от него двумя двоеточиями ::.

Примечание 4 (Julia). Если поле может принимать как значение некоторого типа Type, так и пустое значение nothing (аналог null в других языках программирования), то в Julia надо определить тип этого поля как объединение типа Type и особого типа Nothing. Например, описать поле top стека можно следующим образом: top::Union{StackNode, Nothing}, что позволит при создании стека инициализировать его поле top значением nothing: Stack(nothing).

Примечание 5 (Julia). При определении типа можно выбрать, делать его изменяемым (mutable) или нет. Неизменяемость типа означает, что значения его полей нельзя изменять, хотя можно изменять объекты, на которые указывают эти поля. Например, если в типе есть поле для хранения массива, то даже в случае неизменяемости этого типа содержимое массива можно изменить, поскольку это не требует изменения ссылки на массив в данном типе. Желательно делать типы неизменяемыми, поскольку это повышает эффективность работы с ними. Функции, которые изменяют поля mutable-структур, обычно имеют имена, оканчивающиеся символом ! (восклицательный знак). В случае стека подобные имена рекомендуется использовать для операций Push, Pop и Clear (push!, pop!, clear!)

Примечание 6 (Julia). При разработке программ, использующих задачник, в качестве целого типа надо применять тип Int, а в качестве вещественного — Float64.

[Julia]

struct StackNode
    data::Int
    next::Union{StackNode, Nothing}
end

mutable struct Stack
    top::Union{StackNode, Nothing}
end

Stack() = Stack(nothing)

function push!(s::Stack, data::Int)
    s.top = StackNode(data, s.top)
end

# Implement other functions for the Stack struct

function stackTest(s::Stack)
    # Implement the stackTest function
end

Примечание (PascalABC.NET). Перед закрытыми членами класса следует указывать модификатор private. Чтобы сделать член класса открытым, надо указать перед ним модификатор public. Модификатор protected разрешает доступ к члену класса для любых производных классов. Имеется также модификатор internal, который обеспечивает видимость члена класса внутри сборки, в которой класс описан (перед группой начальных членов можно не указывать модификатор, в этом случае для них подразумевается модификатор internal). Любой из перечисленных модификаторов действует до появления нового модификатора.

[PascalABC.NET]

type
  StackNode = class
    data: integer;
    next: StackNode;
    constructor Create(data: integer; next: StackNode);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  Stack = class
  private
    top: StackNode := nil;
  public
    procedure Push(data: integer);
    begin
      top := new StackNode(data, top);
    end;
    // Реализуйте остальные методы класса Stack
  end;

procedure StackTest(s: Stack);
begin
  // Реализуйте функцию StackTest
end;

OOP0Begin2°. Наследование.

Принцип наследования является вторым принципом ООП; он позволяет создавать иерархии родственных классов. Класс, от которого порождается (наследуется) новый класс, называется классом-предком, или базовым классом, или суперклассом. Порожденный класс называется классом-потомком, или производным классом, или субклассом.

Реализовать класс StackC, который является потомком класса Stack (реализованного в задании OOP0Begin1) и обеспечивает дополнительную возможность: определение текущего количества элементов в стеке. Для этого добавить в класс StackC новое закрытое поле cnt целого типа, открытый метод Count без параметров, возвращающий значение поля cnt, и переопределить методы Push, Pop и Clear таким образом, чтобы они, помимо действий, предусмотренных в классе-предке Stack, соответствующим образом корректировали значение поля cnt. Определить конструктор, в котором задается начальное значение поля cnt, равное 0. Кроме того, переопределить метод ToStr, добавив к строке, возвращаемой методом ToStr предка, значение поля cnt в квадратных скобках, например, «12–34–end[2]». Для пустого стека метод ToStr должен возвращать строку «end[0]».

Тестирование разработанной системы классов. Дана строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека по тем же правилам, что и в задании OOP0Begin1. Команды ToStr и Count в набор не входят.

Создать объект s типа StackC и выполнить для него все команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name по тем же правилам, что и в OOP0Begin1. Как и в задании OOP0Begin1, команды для стека должны вызываться во вспомогательной функции StackTest, которая должна иметь параметр-ссылку типа StackC. За исключением типа своего параметра функция StackTest не должна отличаться от варианта, реализованного в задании OOP0Begin1.

Примечание (C++). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется имя базового класса Stack::). Заметим, что в описанной реализации класса StackC, а точнее, в реализации системы классов Stack и StackC имеется недочет, который будет исправлен в следующем задании (компилятор C++ этот недочет не выявляет).

[C++]

class StackNode
{
public:
    int data;
    shared_ptr<StackNode> next;
    StackNode(int data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
};

class Stack
{
    shared_ptr<StackNode> top = nullptr;
public:
    void Push(int data)
    {
        top = make_shared<StackNode>(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin1 task

};

class StackC : public Stack
{
private:
    int cnt = 0;
public:
    void Push(int data);
    // Implement other methods of the StackC class

};

void StackC::Push(int data)
{
    Stack::Push(data);
    cnt++;
}

void StackTest(shared_ptr<StackC> s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin1 task

}

Примечание (C#). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется ключевое слово base). При подобном переопределении методов в производном классе компилятор выдает предупреждение вида «StackC.Push(int) hides inherited member Stack.Push(int)». Это свидетельствует о недочете в реализации класса StackC (а точнее, о недочете в реализации системы классов Stack и StackC), который будет исправлен в следующем задании.

[C#]

internal class StackNode
{
    internal int data;
    internal StackNode next;
    internal StackNode(int data, StackNode next)
    {
        this.data = data;
        this.next = next;
    }
}

public class Stack
{
    private StackNode top = null;
    public void Push(int data)
    {
        top = new StackNode(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin1 task

}

public class StackC: Stack
{
    private int cnt = 0;
    public void Push(int data)
    {
        cnt++;
        base.Push(data);
    }
    // Implement other methods of the StackC class

}

static void StackTest(StackC s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin1 task

}

Примечание (Java). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется ключевое слово super).

[Java]

class StackNode {
    public int data;
    public StackNode next;

    public StackNode(int data, StackNode next) {
        this.data = data;
        this.next = next;
    }
}

class Stack {
    private StackNode top = null;

    public void push(int data) {
        top = new StackNode(data, top);
    }

    // Use the implementation of the Stack class
    // from the OOP0Begin1 task

}

class StackC extends Stack {
    private int cnt = 0;

    public void push(int data) {
        super.push(data);
        cnt++;
    }

    // Implement other methods of the StackC class

}

public class MyTask extends PT
{
    public static void stackTest(StackC s) throws IOException {
        // Use the body of the stackTest function
        // from the OOP0Begin1 task

    }

Примечание (Python). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется ключевое слово super).

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data, self.__top)

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

class StackC(Stack):
    def __init__(self):
        super().__init__()
        self.__cnt = 0

    def push(self, data):
        super().push(data)
        self.__cnt += 1

    # Implement other methods of the StackC class

def stackTest(s):
    pass
    # Use the body of the stackTest function
    # from the OOP0Begin1 task

Примечание (Ruby). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется ключевое слово super).

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data,@top)
    end

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

end

class StackC < Stack
    def initialize
        super
        @cnt = 0
    end

    def push(data)
        super
        @cnt += 1
    end

    # Implement other methods of the StackC class

end

def stackTest(s)
    # Use the body of the stackTest function
    # from the OOP0Begin1 task

end

Примечание (Julia). В Julia отсутствует механизм порождения новых классов от неабстрактных существующих классов, что препятствует реализации принципа наследования в полном объеме. Поэтому при порождении нового класса от существующего неабстрактного (конкретного) класса обычно применяется композиция: объект конкретного базового класса (предка) описывается как поле производного класса (потомка).

[Julia]

struct StackNode
    data::Int
    next::Union{StackNode, Nothing}
end

mutable struct Stack
    top::Union{StackNode, Nothing}
end

mutable struct StackC
    stack::Stack
    cnt::Int
end

Stack() = Stack(nothing)

function push!(s::Stack, data::Int)
    s.top = StackNode(data, s.top)
end

# Use the implementation of the Stack struct
# from the OOP0Begin1 task

StackC() = StackC(Stack(), 0)

function push!(s::StackC, data::Int)
    s.cnt += 1
    push!(s.stack, data)
end

# Implement other functions for the StackC struct

function stackTest(s::StackC)
    # Use the body of the stackTest function
    # from the OOP0Begin1 task
end

Примечание (PascalABC.NET). В заготовке программы приведен образец наследования производного класса от базового класса. Кроме того, показано, как вызывать методы базового класса из методов производного класса (используется ключевое слово inherited). Следует заметить, что такой вариант переопределения методов является не вполне правильным, поскольку не обеспечивает их позднее связывание. Этот недочет будет исправлен в следующем задании.

[PascalABC.NET]

type
  StackNode = class
    data: integer;
    next: StackNode;
    constructor Create(data: integer; next: StackNode);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  Stack = class
  private
    top: StackNode := nil;
  public
    procedure Push(data: integer);
    begin
      top := new StackNode(data, top);
    end;
    // Используйте реализацию класса Stack из задания OOP0Begin1
  end;

  StackC = class(Stack)
  private
    cnt: integer := 0;
  public
    procedure Push(data: integer);
    begin
      cnt += 1;
      inherited Push(data);
    end;
    // Реализуйте остальные методы класса StackC
  end;

procedure StackTest(s: StackC);
begin
  // Используйте тело функции StackTest из задания OOP0Begin1
end;

OOP0Begin3°. Полиморфизм.

В задании предполагается, что уже реализованы классы Stack и StackC, описанные в заданиях OOP0Begin1 и OOP0Begin2. Изменить, при необходимости, реализацию методов Push, Pop, Clear и ToStr этих классов таким образом, чтобы при вызове их методов выполнялось позднее связывание, (связывание на этапе выполнения), благодаря которому выбор варианта вызываемого метода определяется фактическим типом объекта (указанным при его создании), а не базовым типом, использованным при его описании. Тем самым обеспечивается принцип полиморфизма — третий принцип ООП, позволяющий различным образом обрабатывать объекты разных типов, вызывая для них методы с одинаковыми именами. Для языков с динамической типизацией принцип полиморфизма обеспечивается автоматически для всех классов; для языков со статической типизацией могут потребоваться дополнительные действия при определении тех методов, для которых требуется реализовать позднее связывание (такие методы называются виртуальными).

Тестирование разработанной системы классов. Дана строка Class (тип создаваемого объекта), строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека по тем же правилам, что и в задании OOP0Begin1. Команды ToStr и Count в набор не входят. Строка Class может принимать одно из двух значений: «Stack» или «StackC».

Создать объект s типа, указанного в строке Class, и выполнить для него все команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name по тем же правилам, что и в OOP0Begin1.

Для выполнения всех команд использовать один вызов функции StackTest с параметром требуемого типа; эта функция не должна отличаться от варианта, реализованного в задании OOP0Begin1 (в частности, ее формальный параметр должен быть ссылкой на тип Stack).

Примечание 1 (C++). Для реализации позднего связывания в языке C++ необходимо сделать виртуальными те методы, который по-разному реализованы в классах Stack и StackC: при первом описании этих методов надо в начале их заголовков указать модификатор virtual (для переопределении методов этот модификатор не указывается).

Примечание 2 (C++). Для повышения наглядности и надежности кода при переопределении методов в производных классах рекомендуется указывать в конце их заголовков необязательный модификатор override. В этом случае компилятор проверит наличие данного метода в базовом классе и при его отсутствии выведет сообщение об ошибке.

[C++]

class StackNode
{
public:
    int data;
    shared_ptr<StackNode> next;
    StackNode(int data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
};

class Stack
{
    shared_ptr<StackNode> top = nullptr;
public:
    virtual void Push(int data)
    {
        top = make_shared<StackNode>(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin1 task
    // and add the "virtual" keyword to headers
    // of the Pop, Clear and ToStr methods

};

class StackC : public Stack
{
private:
    int cnt = 0;
public:
    void Push(int data) override;
    // Use the implementation of the StackC class
    // from the OOP0Begin2 task
    // and add the "override" keyword to
    // the Pop, Clear and ToStr methods

};

void StackC::Push(int data)
{
    Stack::Push(data);
    cnt++;
}

void StackTest(shared_ptr<Stack> s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin2 task

}

Примечание (C#). Для реализации позднего связывания в языке C# необходимо сделать виртуальными те методы, который по-разному реализованы в классах Stack и StackC: при их описании надо указывать после слова public модификаторы virtual (для первой реализации метода) или override (при переопределении метода в производных классах).

[C#]

internal class StackNode
{
    internal int data;
    internal StackNode next;
    internal StackNode(int data, StackNode next)
    {
        this.data = data;
        this.next = next;
    }
}

public class Stack
{
    private StackNode top = null;
    public virtual void Push(int data)
    {
        top = new StackNode(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin1 task
    // and add the "virtual" keyword to headers
    // of the Pop, Clear and ToStr methods

}

public class StackC: Stack
{
    private int cnt = 0;
    public override void Push(int data)
    {
        cnt++;
        base.Push(data);
    }
    // Use the implementation of the StackC class
    // from the OOP0Begin2 task
    // and add the "override" keyword to headers
    // of the Pop, Clear and ToStr methods

}

static void StackTest(Stack s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin2 task

}

Примечание 1 (Java). Поскольку в языке Java все методы классов по умолчанию обеспечивают позднее связывание, для решения задачи достаточно использовать систему классов, разработанную в задании OOP0Begin2, не внося в нее никаких изменений.

Примечание 2 (Java). Для повышения наглядности и надежности кода при переопределении методов в производных классах рекомендуется указывать в начале их заголовков необязательный модификатор @Override. В этом случае компилятор проверит наличие данного метода в базовом классе и при его отсутствии выведет сообщение об ошибке.

[Java]

class StackNode {
    public int data;
    public StackNode next;

    public StackNode(int data, StackNode next) {
        this.data = data;
        this.next = next;
    }
}

class Stack {
    private StackNode top = null;

    public void push(int data) {
        top = new StackNode(data, top);
    }

    // Use the implementation of the Stack class
    // from the OOP0Begin1 task

}

class StackC extends Stack {
    private int cnt = 0;

    @Override
    public void push(int data) {
        super.push(data);
        cnt++;
    }

    // Use the implementation of the StackC class
    // from the OOP0Begin2 task
    // and add the "@Override" to
    // the pop, clear and toStr methods

}

public class MyTask extends PT
{
    public static void stackTest(Stack s) throws IOException {
        // Use the body of the stackTest function
        // from the OOP0Begin2 task

    }

Примечание (Python). Язык Python является языком с динамической типизацией, поэтому для выполнения этого задания не требуется вносить какие-либо изменения в классы, разработанные в заданиях OOP0Begin1 и OOP0Begin2.

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data, self.__top)

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

class StackC(Stack):
    def __init__(self):
        super().__init__()
        self.__cnt = 0

    def push(self, data):
        super().push(data)
        self.__cnt += 1

    # Use the implementation of the StackC class
    # from the OOP0Begin2 task

def stackTest(s):
    pass
    # Use the body of the stackTest function
    # from the OOP0Begin2 task

Примечание (Ruby). Язык Ruby является языком с динамической типизацией, поэтому для выполнения этого задания не требуется вносить какие-либо изменения в классы, разработанные в заданиях OOP0Begin1 и OOP0Begin2.

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data,@top)
    end

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

end

class StackC < Stack
    def initialize
        super
        @cnt = 0
    end

    def push(data)
        super
        @cnt += 1
    end

    # Use the implementation of the StackC class
    # from the OOP0Begin2 task

end

def stackTest(s)
    # Use the body of the stackTest function
    # from the OOP0Begin2 task

end

Примечание 1 (Julia). В Julia полноценная иерархия наследования возможна только между абстрактными типами, поскольку от конкретного типа нельзя породить новый тип-потомок. Поэтому для включения нескольких конкретных типов в единую иерархию необходимо породить их от общего абстрактного предка. Такой подход позволит определять функции с параметром — абстрактным типом, в качестве которого можно будет указывать любого конкретного потомка. Например, можно реализовать функцию stackTest с параметром типа AbstractStack, указав этот абстрактный тип в качестве предка типов Stack и StackC, что позволит в дальнейшем вызывать эту функцию как для объектов типа Stack, так и для объектов типа StackC.

Примечание 2 (Julia). Описанный выше способ реализации принципа полиморфизма в Julia является лишь одним из возможных способов (наиболее близким к другим объектно-ориентированным языкам). Возможен и другой способ: описать несколько вариантов одной и той же функции с параметрами разного типа. В этом случае при вызове функции с некоторым параметром выбор варианта функции будет выполняться с учетом фактического типа параметра (таким образом, в этом варианте тоже реализуется механизм позднего связывания, обеспечивающий полиморфное поведение).

[Julia]

struct StackNode
    data::Int
    next::Union{StackNode, Nothing}
end

abstract type AbstractStack
end

mutable struct Stack <: AbstractStack
    top::Union{StackNode, Nothing}
end

mutable struct StackC <: AbstractStack
    stack::Stack
    cnt::Int
end

Stack() = Stack(nothing)

function push!(s::Stack, data::Int)
    s.top = StackNode(data, s.top)
end

# Use the implementation of the Stack struct
# from the OOP0Begin1 task

StackC() = StackC(Stack(), 0)

function push!(s::StackC, data::Int)
    s.cnt += 1
    push!(s.stack, data)
end

# Use the implementation of the StackC struct
# from the OOP0Begin2 task

function stackTest(s::AbstractStack)
    # Use the body of the stackTest function
    # from the OOP0Begin2 task
end

Примечание (PascalABC.NET). Для реализации позднего связывания в языке PascalABC.NET необходимо сделать виртуальными те методы, который по-разному реализованы в классах Stack и StackC: при их описании надо указывать после заголовка метода модификаторы virtual (для первой реализации метода) или override (при переопределении метода в производных классах).

[PascalABC.NET]

type
  StackNode = class
    data: integer;
    next: StackNode;
    constructor Create(data: integer; next: StackNode);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  Stack = class
  private
    top: StackNode := nil;
  public
    procedure Push(data: integer); virtual;
    begin
      top := new StackNode(data, top);
    end;
    // Используйте реализацию класса Stack из задания OOP0Begin1
    // и добавьте ключевое слово "virtual"
    // к заголовкам методов Pop, Clear и ToStr
  end;

  StackC = class(Stack)
  private
    cnt: integer := 0;
  public
    procedure Push(data: integer); override;
    begin
      cnt += 1;
      inherited Push(data);
    end;
    // Используйте реализацию класса StackC из задания OOP0Begin2
    // и добавьте ключевое слово "override"
    // к заголовкам методов Pop, Clear и ToStr
  end;

procedure StackTest(s: Stack);
begin
  // Используйте тело функции StackTest из задания OOP0Begin2
end;

Дополнительные приемы ООП

OOP0Begin4°. Получение информации о типе времени выполнения.

Если в программе требуется вызвать метод, отсутствующий в базовом классе, но имеющийся в каком-либо производном классе, то механизм позднего связывания неприменим, и необходимо явным образом анализировать информацию о типе времени выполнения объекта (RTTI — Run-Time Type Information).

В данном задании предполагается, что уже реализованы классы Stack и StackC, описанные в заданиях OOP0Begin1–OOP0Begin3.

Дана строка Class (тип создаваемого объекта), строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека по тем же правилам, что и в задании OOP0Begin1. Команда ToStr в набор не входит, однако команда Count может входить. Строка Class может принимать одно из двух значений: «Stack» или «StackC».

Создать объект s типа, указанного в строке Class, и выполнить для него все команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name по тем же правилам, что и в OOP0Begin1.

Команда Count обрабатывается по следующим дополнительным правилам: если объект имеет тип времени выполнения Stack, то в строку файла записывается текст «Count: Method not available», если объект имеет тип времени выполнения StackC, то строка файла должна содержать имя «Count», двоеточие, пробел и значение, которое вернул метод Count (например, «Count: 5»; вызывать метод ToStr и выводить его результат в данном случае не требуется).

Для выполнения всех команд использовать один вызов функции StackTest с параметром требуемого типа; эта функция не должна отличаться от варианта, реализованного в задании OOP0Begin3, за исключением фрагмента, обрабатывающего команду Count.

Примечание (C++). Для определения RTTI в языке C++ предусмотрены операции dynamic_cast (для обычных указателей) и dynamic_pointer_cast (для умных указателей). Например, если умный указатель s описан как shared_ptr<Stack>, но фактический тип связанного с ним объекта *s равен StackC (или тип StackC является одним из классов-предков фактического типа объекта *s), то выражение dynamic_pointer_cast<StackC>(s) вернет тот же умный указатель s, но приведенный к типу shared_ptr<StackC>. Если такое приведение невозможно, то будет возвращен нулевой указатель.

[C++]

class StackNode
{
public:
    int data;
    shared_ptr<StackNode> next;
    StackNode(int data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
};

class Stack
{
    shared_ptr<StackNode> top = nullptr;
public:
    virtual void Push(int data)
    {
        top = make_shared<StackNode>(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin3 task

};

class StackC : public Stack
{
private:
    int cnt = 0;
public:
    void Push(int data) override;
    // Use the implementation of the StackC class
    // from the OOP0Begin3 task

};

void StackC::Push(int data)
{
    Stack::Push(data);
    cnt++;
}

void StackTest(shared_ptr<Stack> s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin3 task
    // and add a new fragment related
    // to the processing of the Count command

}

Примечание (C#). Для определения RTTI в языке C# предусмотрены операции is и as. Например, если объект s описан как Stack, но фактический тип объекта s равен StackC (или тип StackC является одним из классов-предков фактического типа объекта s), то выражение s is StackC вернет true, а выражение s as StackC вернет тот же объект s, но приведенный к типу StackC. Если такое приведение невозможно, то выражение s is StackC вернет false, а выражение s as StackC — значение null).

[C#]

internal class StackNode
{
    internal int data;
    internal StackNode next;
    internal StackNode(int data, StackNode next)
    {
        this.data = data;
        this.next = next;
    }
}

public class Stack
{
    private StackNode top = null;
    public virtual void Push(int data)
    {
        top = new StackNode(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin3 task

}

public class StackC: Stack
{
    private int cnt = 0;
    public override void Push(int data)
    {
        cnt++;
        base.Push(data);
    }
    // Use the implementation of the StackC class
    // from the OOP0Begin3 task

}

static void StackTest(Stack s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin3 task
    // and add a new fragment related
    // to the processing of the Count command

}

Примечание (Java). Для определения RTTI в языке Java предусмотрена операция instanceof. Например, если объект s описан как Stack, но его фактический тип равен StackC (или тип StackC является одним из классов-предков фактического типа объекта s), то выражение s instanceof StackC вернет true, в противном случае оно вернет false. Если будет возвращено значение true, то объект s можно безопасно преобразовать к типу StackC обычной операцией преобразования (StackC)s; в случае значения false попытка такого преобразования приведет к возбуждению исключения ClassCastException.

[Java]

class StackNode {
    public int data;
    public StackNode next;

    public StackNode(int data, StackNode next) {
        this.data = data;
        this.next = next;
    }
}

class Stack {
    private StackNode top = null;

    public void push(int data) {
        top = new StackNode(data, top);
    }

    // Use the implementation of the Stack class
    // from the OOP0Begin3 task

}

class StackC extends Stack {
    private int cnt = 0;

    @Override
    public void push(int data) {
        super.push(data);
        cnt++;
    }

    // Use the implementation of the StackC class
    // from the OOP0Begin3 task

}

public class MyTask extends PT
{
    public static void stackTest(Stack s) throws IOException {
        // Use the body of the stackTest function
        // from the OOP0Begin3 task
        // and add a new fragment related
        // to the processing of the Count command

    }

Примечание (Python). В языке Python имеется функция isinstance, позволяющая проверить текущий тип объекта. Например, если тип объекта s равен StackC (или тип StackC является одним из классов-предков типа объекта s), то выражение isinstance(s, StackC) вернет True, в противном случае оно вернет False. Если функция isinstance вернула значение True, то для объекта можно безопасно вызывать все методы данного класса.

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data, self.__top)

    # Use the implementation of the Stack class
    # from the OOP0Begin3 task

class StackC(Stack):
    def __init__(self):
        super().__init__()
        self.__cnt = 0

    def push(self, data):
        super().push(data)
        self.__cnt += 1

    # Use the implementation of the StackC class
    # from the OOP0Begin3 task

def stackTest(s):
    pass
    # Use the body of the stackTest function
    # from the OOP0Begin3 task
    # and add a new fragment related
    # to the processing of the Count command

Примечание (Ruby). В языке Ruby для проверки текущего типа объекта следует использовать операцию ===, первым операндом которой должен быть проверяемый тип. Например, если тип объекта s равен StackC (или тип StackC является одним из классов-предков типа объекта s), то выражение StackC === s (порядок операндов важен) вернет true, в противном случае оно вернет false. Если операция === вернула значение true, то для объекта можно безопасно вызывать все методы данного класса.

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data,@top)
    end

    # Use the implementation of the Stack class
    # from the OOP0Begin3 task

end

class StackC < Stack
    def initialize
        super
        @cnt = 0
    end

    def push(data)
        super
        @cnt += 1
    end

    # Use the implementation of the StackC class
    # from the OOP0Begin3 task

end

def stackTest(s)
    # Use the body of the stackTest function
    # from the OOP0Begin3 task
    # and add a new fragment related
    # to the processing of the Count command

end

Примечание (Julia). В языке Julia имеется функция isa, позволяющая проверить текущий тип объекта. Например, если тип объекта s равен StackC (или тип StackC является одним из типов-предков объекта s), то выражение isa(s, StackC) вернет true, в противном случае оно вернет false. Применение функции isa оправдано при разработке функций, в которых параметры описаны как абстрактные и поэтому могут соответствовать различным конкретным типам-потомкам.

[Julia]

struct StackNode
    data::Int
    next::Union{StackNode, Nothing}
end

abstract type AbstractStack end

mutable struct Stack <: AbstractStack
    top::Union{StackNode, Nothing}
end

mutable struct StackC <: AbstractStack
    stack::Stack
    cnt::Int
end

Stack() = Stack(nothing)

function push!(s::Stack, data::Int)
    s.top = StackNode(data, s.top)
end

# Use the implementation of the Stack struct
# from the OOP0Begin3 task

StackC() = StackC(Stack(), 0)

function push!(s::StackC, data::Int)
    s.cnt += 1
    push!(s.stack, data)
end

# Use the implementation of the StackC struct
# from the OOP0Begin3 task

function stackTest(s::AbstractStack)
    # Use the body of the stackTest function
    # from the OOP0Begin3 task
    # and add a new fragment related
    # to the processing of the Count command
end

Примечание (PascalABC.NET). Для определения RTTI в языке PascalABC.NET предусмотрены операции is и as. Например, если объект s описан как Stack, но фактический тип объекта s равен StackC (или тип StackC является одним из классов-предков фактического типа объекта s), то выражение s is StackC вернет True, а выражение s as StackC вернет тот же объект s, но приведенный к типу StackC. Если такое приведение невозможно, то выражение s is StackC вернет False, а выражение s as StackC — значение nil).

[PascalABC.NET]

type
  StackNode = class
    data: integer;
    next: StackNode;
    constructor Create(data: integer; next: StackNode);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  Stack = class
  private
    top: StackNode := nil;
  public
    procedure Push(data: integer); virtual;
    begin
      top := new StackNode(data, top);
    end;
    // Используйте реализацию класса Stack из задания OOP0Begin3
  end;

  StackC = class(Stack)
  private
    cnt: integer := 0;
  public
    procedure Push(data: integer); override;
    begin
      cnt += 1;
      inherited Push(data);
    end;
    // Используйте реализацию класса StackC из задания OOP0Begin3
  end;

procedure StackTest(s: Stack);
begin
  // Используйте тело функции StackTest из задания OOP0Begin3
  // и добавьте в него фрагмент, связанный с обработкой команды Count
end;

OOP0Begin5°. Генерация и обработка исключений.

Исключения (exceptions) являются универсальным механизмом, который дает возможность объектам стандартным образом оповещать внешнюю программу об особых ситуациях, возникающих при выполнении их методов, а внешней программе — гибко реагировать на возникшие особые ситуации. Как правило, конкретные исключения реализуются в виде иерархии специальных классов, которую можно расширять, наследуя новые классы исключений от существующих.

Изменить методы Pop и Peek классов Stack и StackC, описанных в заданиях OOP0Begin1–OOP0Begin3, таким образом, чтобы при попытке их вызова для пустого стека возбуждалось исключение StackException с текстовым сообщением «Empty stack in Pop» для метода Pop и «Empty stack in Peek» для метода Peek.

Тестирование разработанной системы классов. Дана строка Class (тип создаваемого объекта), строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека по тем же правилам, что и в задании OOP0Begin1. Команды ToStr и Count в набор не входят. Строка Class может принимать одно из двух значений: «Stack» или «StackC».

Создать объект s типа, указанного в строке Class, и выполнить для него команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name по тем же правилам, что и в OOP0Begin1.

Если в результате выполнения команды Pop или Peek будет возбуждено исключение, то записать в очередную строку файла текст сообщения вида «Pop: Empty stack in Pop» или «Peek: Empty stack in Peek», закрыть файл и прервать обработку команд.

Для выполнения всех команд использовать один вызов функции StackTest (с параметром требуемого типа), модифицировав ее вариант, реализованный в задании OOP0Begin3 или OOP0Begin4.

Примечание (C++). Исключения в языке C++, как правило, являются классами-потомками стандартного класса exception. Чтобы можно было получить дополнительную информацию о возникшем исключении, в классе, производном от exception, надо переопределить виртуальный метод what(). Для возбуждения исключения используется оператор throw, в котором вызывается конструктор требуемого класса-исключения (обычно в классе-исключении определяется конструктор со строковым параметром, который содержит дополнительное описание выявленной особой ситуации).

Для перехвата исключения во внешней программе используется конструкция try-catch. Блок try содержит операторы, в которых может возникнуть исключение, блоки catch (их может быть несколько) содержат обработчики различных исключений (тип обрабатываемого исключения указывается в заголовке блока catch и заключается в круглые скобки). Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать блок catch с заголовком catch(exception& ex).

[C++]

class StackNode
{
public:
    int data;
    shared_ptr<StackNode> next;
    StackNode(int data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
};

// Implementation of the StackException class
// containing the constructor with a string parameter
// and overriding the what() function
// (you do not need to change this implementation)
class StackException : public exception
{
private:
    string msg;
public:
    StackException(string msg):msg(msg) {}

    const char* what() const throw() override
    {
        return msg.c_str();
    }
};

class Stack
{
    shared_ptr<StackNode> top = nullptr;
public:
    virtual void Push(int data)
    {
        top = make_shared<StackNode>(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin4 task
    // and change the implementation of Pop and Peek methods
    // by adding a throw statement
    // to handle the case of an empty stack

};

class StackC : public Stack
{
private:
    int cnt = 0;
public:
    void Push(int data) override;
    // Use the implementation of the StackC class
    // from the OOP0Begin4 task

};

void StackC::Push(int data)
{
    Stack::Push(data);
    cnt++;
}

void StackTest(shared_ptr<Stack> s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin4 or OOP0Begin3 task
    // and add a try-catch statement to handle
    // the case of an empty stack for Pop and Peek commands

}

Примечание (C#). Исключения в языке C# являются классами-потомками стандартного класса Exception. Для возбуждения исключения используется оператор throw, в котором вызывается конструктор требуемого класса-исключения (обычно ему передается строковый параметр с дополнительным описанием выявленной особой ситуации).

Для перехвата исключения во внешней программе используется конструкция try-catch. Блок try содержит операторы, в которых может возникнуть исключение, блоки catch (их может быть несколько) содержат обработчики различных исключений (тип обрабатываемого исключения указывается в заголовке блока catch и заключается в круглые скобки). Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать блок catch с заголовком catch(Exception ex).

После всех блоков catch может располагаться необязательный блок finally, содержащий операторы, которые выполняются после выхода из try-блока в любом случае (даже если в try-блоке было возбуждено исключение, которое не было обработано в блоках catch).

[C#]

internal class StackNode
{
    internal int data;
    internal StackNode next;
    internal StackNode(int data, StackNode next)
    {
        this.data = data;
        this.next = next;
    }
}

// Implementation of the StackException class
// containing the constructor with a string parameter
// (you do not need to change this implementation)
public class StackException : Exception
{
    public StackException(string message) : base(message)
    { }
}

public class Stack
{
    private StackNode top = null;
    public virtual void Push(int data)
    {
        top = new StackNode(data, top);
    }
    // Use the implementation of the Stack class
    // from the OOP0Begin4 task
    // and change the implementation of Pop and Peek methods
    // by adding a throw statement
    // to handle the case of an empty stack

}

public class StackC: Stack
{
    private int cnt = 0;
    public override void Push(int data)
    {
        cnt++;
        base.Push(data);
    }
    // Use the implementation of the StackC class
    // from the OOP0Begin4 task

}

static void StackTest(Stack s)
{
    // Use the body of the StackTest function
    // from the OOP0Begin4 or OOP0Begin3 task
    // and add a try-catch statement to handle
    // the case of an empty stack for Pop and Peek commands

}

Примечание (Java). Исключения в языке Java являются классами-потомками стандартного класса Exception. Для возбуждения исключения используется оператор throw, в котором вызывается конструктор требуемого класса-исключения (обычно ему передается строковый параметр с дополнительным описанием выявленной особой ситуации).

Для перехвата исключения во внешней программе используется конструкция try-catch. Блок try содержит операторы, в которых может возникнуть исключение, блоки catch (их может быть несколько) содержат обработчики различных исключений (тип обрабатываемого исключения указывается в заголовке блока catch и заключается в круглые скобки). Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать блок catch с заголовком catch(Exception ex).

После всех блоков catch может располагаться необязательный блок finally, содержащий операторы, которые выполняются после выхода из try-блока в любом случае (даже если в try-блоке было возбуждено исключение, которое не было обработано в блоках catch).

[Java]

class StackNode {
    public int data;
    public StackNode next;

    public StackNode(int data, StackNode next) {
        this.data = data;
        this.next = next;
    }
}

// Implementation of the StackException class
// containing the constructor with a string parameter
// (you do not need to change this implementation)
class StackException extends Exception {
    public StackException(String s) {
        super(s);
    }
}

class Stack {
    private StackNode top = null;

    public void push(int data) {
        top = new StackNode(data, top);
    }

    // Use the implementation of the Stack class
    // from the OOP0Begin4 task
    // and change the implementation of pop and peek methods
    // by adding a throw statement
    // to handle the case of an empty stack

}

class StackC extends Stack {
    private int cnt = 0;

    @Override
    public void push(int data) {
        super.push(data);
        cnt++;
    }

    // Use the implementation of the StackC class
    // from the OOP0Begin4 task

}

public class MyTask extends PT
{
    public static void stackTest(Stack s) throws IOException {
        // Use the body of the stackTest function
        // from the OOP0Begin4 or OOP0Begin3 task
        // and add a try-catch statement to handle
        // the case of an empty stack for Pop and Peek commands

    }

Примечание (Python). Исключения в языке Python являются классами-потомками стандартного класса Exception. Для возбуждения исключения используется оператор raise, в котором вызывается конструктор требуемого класса-исключения (обычно ему передается строковый параметр с дополнительным описанием выявленной особой ситуации). Если в классе-исключении определить функцию __str__, возвращающую его строковое описание, то для получения этого строкового описания будет достаточно преобразовать исключение к типу str.

Для перехвата исключения во внешней программе используется конструкция try-except. Блок try содержит операторы, в которых может возникнуть исключение, блоки except (их может быть несколько) содержат обработчики различных исключений (тип обрабатываемого исключения указывается в заголовке блока except). Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать блок except с заголовком except Exception as ex.

После всех блоков except может располагаться необязательный блок finally, содержащий операторы, которые выполняются после выхода из try-блока в любом случае (даже если в try-блоке было возбуждено исключение, которое не было обработано в блоках except).

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

# Implementation of the StackException class
# containing the constructor with a string parameter
# and overriding the __str__ function
# (you do not need to change this implementation)
class StackException(Exception):
    def __init__(self, msg):
        self.__msg = msg

    def __str__(self):
        return self.__msg

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data,self.__top)

    # Use the implementation of the Stack class
    # from the OOP0Begin4 task
    # and change the implementation of pop and peek methods
    # by adding a raise statement
    # to handle the case of an empty stack

class StackC(Stack):
    def __init__(self):
        super().__init__()
        self.__cnt = 0

    def push(self, data):
        super().push(data)
        self.__cnt += 1

    # Use the implementation of the StackC class
    # from the OOP0Begin4 task

def stackTest(s):
    pass
    # Use the body of the stackTest function
    # from the OOP0Begin4 or OOP0Begin3 task
    # and add a try-except statement to handle
    # the case of an empty stack for Pop and Peek commands

Примечание (Ruby). Исключения в языке Ruby являются классами-потомками стандартного класса StandardError. Для возбуждения исключения предназначен оператор raise, который обычно используется в виде raise ExcepType, msg, где ExcepType — тип возбуждаемого исключения, а msg — строка, содержащая описание возникшей особой ситуации (эта строка будет передана созданному объекту-исключению и будет доступна с помощью его метода message).

Для перехвата исключения во внешней программе используется конструкция begin-rescue. Блок begin содержит операторы, в которых может возникнуть исключение, блоки rescue (их может быть несколько) содержат обработчики различных исключений (тип обрабатываемого исключения указывается в заголовке блока rescue, например, rescue ExcepType). В конце заголовка блока rescue можно указать имя переменной, в которой будет сохранен объект-исключение, например, rescue ExcepType => ex). Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать блок rescue с заголовком rescue => ex.

После всех блоков rescue может располагаться необязательный блок ensure, содержащий операторы, которые выполняются после выхода из begin-блока в любом случае (даже если в begin-блоке было возбуждено исключение, которое не было обработано в блоках rescue).

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

# Implementation of the StackException class
# (you do not need to change this implementation)
class StackException < StandardError
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data,@top)
    end

    # Use the implementation of the Stack class
    # from the OOP0Begin4 task
    # and change the implementation of pop and peek methods
    # by adding a raise statement
    # to handle the case of an empty stack

end

class StackC < Stack
    def initialize
        super
        @cnt = 0
    end

    def push(data)
        super
        @cnt += 1
    end

    # Use the implementation of the StackC class
    # from the OOP0Begin4 task

end

def stackTest(s)
    # Use the body of the stackTest function
    # from the OOP0Begin4 or OOP0Begin3 task
    # and add a begin-rescue statement to handle
    # the case of an empty stack for Pop and Peek commands

end

Примечание (Julia). Исключения в языке Julia являются типами-потомками стандартного абстрактного типа Exception. Для возбуждения исключения используется функция throw, в которой вызывается конструктор требуемого типа-исключения (обычно ему передается строковый параметр с дополнительным описанием выявленной особой ситуации). Для перехвата исключения во внешней программе используется конструкция try-catch. Блок try содержит операторы, в которых может возникнуть исключение. За ключевым словом catch может указываться переменная, которая будет связана с созданным объектом-исключением: catch ex. Для определения типа исключения ex в блоке catch обычно используется функция isa.

После блока catch может располагаться необязательный блок finally, содержащий операторы, которые выполняются после выхода из try-блока в любом случае (даже если в try-блоке было возбуждено исключение, которое не было обработано в блоке catch).

[Julia]

struct StackNode
    data::Int
    next::Union{StackNode, Nothing}
end

# Implementation of the StackException struct
# (you do not need to change this implementation)
struct StackException <: Exception
    msg::String
end

abstract type AbstractStack
end

mutable struct Stack <: AbstractStack
    top::Union{StackNode, Nothing}
end

mutable struct StackC <: AbstractStack
    stack::Stack
    cnt::Int
end

Stack() = Stack(nothing)

function push!(s::Stack, data::Int)
    s.top = StackNode(data, s.top)
end

# Use the implementation of the Stack struct
# from the OOP0Begin4 task
# and change the implementation
# of the pop! and peek functions
# by adding a throw function
# to handle the case of an empty stack

StackC() = StackC(Stack(), 0)

function push!(s::StackC, data::Int)
    s.cnt += 1
    push!(s.stack, data)
end

# Use the implementation of the StackC class
# from the OOP0Begin4 task

function stackTest(s::AbstractStack)
    # Use the body of the stackTest function
    # from the OOP0Begin4 or OOP0Begin3 task
    # and add a try-catch statement to handle
    # the case of an empty stack for Pop and Peek commands
end

Примечание (PascalABC.NET). Исключения в языке PascalABC.NET являются классами-потомками стандартного класса System.Exception. Для возбуждения исключения используется оператор raise, в котором вызывается конструктор требуемого класса-исключения (обычно ему передается строковый параметр с дополнительным описанием выявленной особой ситуации).

Для перехвата исключения во внешней программе используется конструкция try-except. Блок try содержит операторы, в которых может возникнуть исключение, блок except содержит один или более обработчиков различных исключений (тип обрабатываемого исключения указывается в заголовке обработчика исключения, который имеет вид on тип_исключения do Для перехвата исключения любого типа с возможностью доступа к созданному объекту-исключению ex достаточно использовать обработчик с заголовком on ex: System.Exception do (при наличии других обработчиков данный обработчик должен указываться последним, чтобы позволить другим обработчикам обработать связанные с ними специальные типы исключений).

Конструкции try-except могут быть вложенными. Кроме того, конструкцию try-except можно поместить в раздел try особой конструкции try-finally. В разделе finally этой конструкции указываются операторы, которые выполняются после выхода из try-блока в любом случае (даже если в try-блоке было возбуждено исключение, которое не было обработано во вложенной конструкции try-except).

[PascalABC.NET]

type
  StackNode = class
    data: integer;
    next: StackNode;
    constructor Create(data: integer; next: StackNode);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  // Класс StackException для обработки исключений, связанных
  // со стековыми операциями (этот класс не требуется изменять)
  StackException = class(Exception)
  public
    constructor Create(message: string);
    begin
      inherited Create(message);
    end;
  end;

  Stack = class
  private
    top: StackNode := nil;
  public
    procedure Push(data: integer); virtual;
    begin
      top := new StackNode(data, top);
    end;
    // Используйте реализацию класса Stack из задания OOP0Begin4
    // и измените реализацию методов Pop и Peek,
    // добавив оператор raise для обработки случая пустого стека
  end;

  StackC = class(Stack)
  private
    cnt: integer := 0;
  public
    procedure Push(data: integer); override;
    begin
      cnt += 1;
      inherited Push(data);
    end;
    // Используйте реализацию класса StackC из задания OOP0Begin4
  end;

procedure StackTest(s: Stack);
begin
  // Используйте тело функции StackTest из заданий OOP0Begin4
  // или OOP0Begin3 и добавьте конструкцию try-except
  // для обработки случая пустого стека при выполнении команд Pop и Peek
end;

OOP0Begin6°. Шаблоны функций и шаблоны классов.

Шаблонные, или обобщенные классы (templates, generics) имеются в языках со статической типизацией; они, в частности, упрощают реализацию коллекций, позволяющих хранить элементы определенного типа (этот тип при описании класса задается в виде обобщенного параметра T). Наряду с шаблонными классами в таких языках можно использовать шаблонные функции. В языках с динамической типизацией шаблонные классы и функции отсутствуют.

Преобразовав при необходимости классы StackNode, Stack и StackC в шаблонные классы (класс StackNode описан в задании OOP0Begin1, классы Stack и StackC описаны, а затем модифицированы, в заданиях OOP0Begin1–OOP0Begin5), обеспечить возможность работы со стеком, содержащим целочисленные, символьные или строковые данные (предполагается, что все элементы стека имеют одинаковый тип).

Тестирование разработанной системы классов. Дана строка Class (тип создаваемого объекта), символ Type (тип элементов стека), строка Name (имя файла), целое число N и набор из N элементов данных, каждый из которых определяет одну из команд стека по тем же правилам, что и в задании OOP0Begin1, за исключением того, что после строки «Push» указывается элемент данных типа, определяемого символом Type:

• «I» — целочисленный тип,

• «C» — символьный тип,

• «S» — строковый тип.

Команды ToStr и Count в набор не входят; набор команд также не содержит команд Pop и Peek, вызываемых для пустого стека. Строка Class может принимать одно из двух значений: «Stack» или «StackC».

Создать объект s типа, указанного в строке Class, с элементами типа, определяемого символом Type, и выполнить для него команды из исходного набора, записывая информацию о выполнении команд в текстовый файл с именем Name по тем же правилам, что и в OOP0Begin1.

Для выполнения всех команд использовать один вызов функции StackTest (с параметром требуемого типа), преобразовав при необходимости ее вариант, реализованный в одном из предыдущих заданий, в шаблонную функцию.

Примечание 1 (C++). При реализации метода ToStr следует использовать строковый поток вывода ostringstream. Это позволит единообразно формировать требуемое строковое описание и для числовых, и для символьных, и для строковых элементов стека.

Примечание 2 (C++). Поскольку поток ввода pt автоматически определяет тип вводимых данных, выполнение команды Push для шаблонного класса Stack<T> в шаблонной функции StackTest не будет ничем отличаться от выполнения этой команды для реализованного в предыдущих заданиях стека целых чисел.

[C++]

template <typename T>
class StackNode
{
public:
    T data;
    shared_ptr<StackNode> next;
    StackNode(T data, shared_ptr<StackNode> next) :
        data(data), next(next) {}
};

template <typename T>
class Stack
{
    shared_ptr<StackNode<T> > top = nullptr;
public:
    virtual void Push(T data)
    {
        top = make_shared<StackNode<T> >(data, top);
    }
    // Similarly, change the implementation of all methods
    // of the Stack class from the OOP0Begin5 task

};

template <typename T>
class StackC : public Stack<T>
{
private:
    int cnt = 0;
public:
    void Push(T data) override;
    // Similarly, change the implementation of all methods
    // of the StackC class from the OOP0Begin5 task

};

template <typename T>
void StackC<T>::Push(T data)
{
    Stack<T>::Push(data);
    cnt++;
}

template <typename T>
void StackTest(shared_ptr<Stack<T> > s)
{
    // Change the implementation of the StackTest method
    // from the OOP0Begin5 task

}

Примечание (C#). При реализации фрагмента обобщенного метода StackTest<T>, отвечающего за выполнение команды Push, необходимо вызывать различные методы ввода (GetInt, GetChar, GetString) в зависимости от фактического значения обобщенного параметра T. Для проверки значения параметра T можно использовать условие вида typeof(T) == typeof(<конкретный тип>), где в качестве конкретного типа указывается int, char или string.

Кроме того, следует учитывать, что вызов метода s.Push(GetInt()) приведет к ошибке компиляции, если объект s описан как объект обобщенного типа Stack<T> (даже если этот вызов выполняется после успешной проверки условия typeof(T) == typeof(int)). Чтобы избежать ошибки компиляции, необходимо выполнить следующее двойное приведение типа: s.Push((T)(object)GetInt()) (такое приведение типа будет успешным только в случае, если параметр T имеет значение int).

[C#]

internal class StackNode<T>
{
    internal T data;
    internal StackNode<T> next;
    internal StackNode(T data, StackNode<T> next)
    {
        this.data = data;
        this.next = next;
    }
}

public class StackException : Exception
{
    public StackException(string message) : base(message)
    { }
}

public class Stack<T>
{
    private StackNode<T> top = null;
    public virtual void Push(T data)
    {
        top = new StackNode<T>(data, top);
    }
    // Similarly, change the implementation of all methods
    // of the Stack class from the OOP0Begin5 task

}

public class StackC<T>: Stack<T>
{
    private int cnt = 0;
    public override void Push(T data)
    {
        cnt++;
        base.Push(data);
    }
    // Similarly, change the implementation of all methods
    // of the StackC class from the OOP0Begin5 task

}

static void StackTest<T>(Stack<T> s)
{
    // Change the implementation of the StackTest method
    // from the OOP0Begin5 task

}

Примечание (Java). При реализации фрагмента обобщенного метода stackTest(Stack<T> s), отвечающего за выполнение команды Push, необходимо вызывать различные методы ввода (getInt, getChar, getString) в зависимости от фактического значения параметра T. К сожалению, получить фактическое значение параметра T в Java сложно (это требует применения механизма рефлексии типов). В подобной ситуации можно использовать следующее не слишком красивое, но работающее решение: передавать в обобщенный метод stackTest дополнительный параметр t типа Class, определяющий тип, которым параметризован метод (в нашем случае значениями параметра t могут быть int.class, char.class или String.class), и в зависимости от значения этого параметра вызывать требуемый метод ввода.

Кроме того, следует учитывать, что вызов метода s.push(getInt()) приведет к ошибке компиляции, если объект s описан как объект обобщенного типа Stack<T>. Чтобы избежать ошибки компиляции, необходимо выполнить следующее двойное приведение типа: s.push((T)(Object)getInt()). Такое приведение типа будет успешным только в случае, если параметр T имеет значение int (это можно установить в методе stackTest путем проверки равенства t == int.class).

[Java]

class StackNode<T> {
    public T data;
    public StackNode<T> next;

    public StackNode(T data, StackNode<T> next) {
        this.data = data;
        this.next = next;
    }
}

class Stack<T> {
    private StackNode<T> top = null;

    public void push(T data) {
        top = new StackNode<T>(data, top);
    }

    // Similarly, change the implementation of all methods
    // of the Stack class from the OOP0Begin5 task

}

class StackC<T> extends Stack<T> {
    private int cnt = 0;

    @Override
    public void push(T data) {
        super.push(data);
        cnt++;
    }

    // Similarly, change the implementation of all methods
    // of the StackC class from the OOP0Begin5 task

}

public class MyTask extends PT
{
    public static <T> void stackTest(Stack<T> s)
        throws IOException {
        // Change the implementation of the stackTest method
        // from the OOP0Begin5 task

    }

Примечание (Python). Язык Python является языком с динамической типизацией, поэтому для выполнения этого задания не требуется вносить какие-либо изменения в классы, разработанные в заданиях OOP0Begin1 и OOP0Begin2.

[Python]

class StackNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class Stack:
    def __init__(self):
        self.__top = None

    def push(self, data):
        self.__top = StackNode(data, self.__top)

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

class StackC(Stack):
    def __init__(self):
        super().__init__()
        self.__cnt = 0

    def push(self, data):
        super().push(data)
        self.__cnt += 1

    # Use the implementation of the StackC class
    # from the OOP0Begin2 task

def stackTest(s):
    pass
    # Change the implementation of the stackTest method
    # from the OOP0Begin3 task

Примечание (Ruby). Язык Ruby является языком с динамической типизацией, поэтому для выполнения этого задания не требуется вносить какие-либо изменения в классы, разработанные в заданиях OOP0Begin1 и OOP0Begin2.

[Ruby]

class StackNode
    def initialize(data, nextNode)
        @data = data
        @next = nextNode
    end

    attr_reader :data, :next
end

class Stack
    def initialize
        @top = nil
    end

    def push(data)
        @top = StackNode.new(data, @top)
    end

    # Use the implementation of the Stack class
    # from the OOP0Begin1 task

end

class StackC < Stack
    def initialize
        super
        @cnt = 0
    end

    def push(data)
        super
        @cnt += 1
    end

    # Use the implementation of the StackC class
    # from the OOP0Begin2 task

end

def stackTest(s)
    # Change the implementation of the stackTest method
    # from the OOP0Begin3 task

end

Примечание 1 (Julia). Чтобы реализовать шаблонные классы или шаблонные функции, в Julia следует передавать типы в качестве особых параметров, заключая их в фигурные скобки. Например, можно определить параметризованную структуру StackNode{T}, в которой использовать тип T при описании ее полей. При необходимости можно уточнять возможные варианты для типа T, используя конструкцию <: для указания допустимого типа-предка для T. При определении параметризованной функции следует указывать выражение where {T} после заголовка функции. Здесь также может использоваться конструкция <: для уточнения типа T.

Примечание 2 (Julia). Поскольку функция задачника get автоматически определяет тип вводимых данных, выполнение команды Push для параметризованного типа Stack{T} в параметризованной функции stackTest ничем не будет отличаться от выполнения этой команды для реализованного в предыдущих заданиях стека целых чисел.

[Julia]

struct StackNode{T}
    data::T
    next::Union{StackNode{T},Nothing}
end

abstract type AbstractStack{T} end

mutable struct Stack{T} <: AbstractStack{T}
    top::Union{StackNode{T},Nothing}
end

mutable struct StackC{T} <: AbstractStack{T}
    stack::Stack{T}
    cnt::Int
end

Stack{T}() where {T} = Stack{T}(nothing)

function push!(s::Stack{T}, data::T) where {T}
    s.top = StackNode(data, s.top)
end

# Similarly, change the implementation of all functions
# for the Stack struct from the OOP0Begin5 task

StackC{T}() where {T} = StackC{T}(Stack{T}(),0)

function push!(s::StackC{T}, data::T) where {T}
    s.cnt += 1
    push!(s.stack, data)
end

# Similarly, change the implementation of all functions
# for the StackC struct from the OOP0Begin5 task

function stackTest(s::AbstractStack{T}) where {T}
    # Change the implementation of the stackTest function
    # from the OOP0Begin5 task
end

Примечание (PascalABC.NET). При реализации фрагмента обобщенного метода StackTest<T>, отвечающего за выполнение команды Push, необходимо вызывать различные методы ввода (ReadInteger, ReadCharr, ReadString) в зависимости от фактического значения обобщенного параметра T. Для проверки значения параметра T можно использовать условие вида typeof(T) == typeof(конкретный_тип), где в качестве конкретного типа указывается integer, char или string.

Кроме того, следует учитывать, что вызов метода s.Push(ReadInteger()) приведет к ошибке компиляции, если объект s описан как объект обобщенного типа Stack<T> (даже если этот вызов выполняется после успешной проверки условия typeof(T) == typeof(integer)). Чтобы избежать ошибки компиляции, необходимо выполнить следующее двойное приведение типа: s.Push(T(object(ReadInteger()))) (такое приведение типа будет успешным только в случае, если параметр T имеет значение integer).

[PascalABC.NET]

type
  StackNode<T> = class
    data: T;
    next: StackNode<T>;
    constructor Create(data: T; next: StackNode<T>);
    begin
      self.data := data;
      self.next := next;
    end;
  end;

  StackException = class(Exception)
  public
    constructor Create(message: string);
    begin
      inherited Create(message);
    end;
  end;

  Stack<T> = class
  private
    top: StackNode<T> := nil;
  public
    procedure Push(data: T); virtual;
    begin
      top := new StackNode<T>(data, top);
    end;
    // Аналогичным образом измените реализацию
    // методов класса Stack из задания OOP0Begin5
  end;

  StackC<T> = class(Stack<T>)
  private
    cnt: integer := 0;
  public
    procedure Push(data: T); override;
    begin
      cnt += 1;
      inherited Push(data);
    end;
    // Аналогичным образом измените реализацию
    // методов класса StackC из задания OOP0Begin5
  end;

procedure StackTest<T>(s: Stack<T>);
begin
  // Измените реализацию процедуры StackTest из задания OOP0Begin5
end;

OOP0Begin7°. Перегрузка операций и приведение объекта к другим типам.

Реализовать класс FracNum, предназначенный для хранения и обработки обыкновенных дробей. Класс FracNum включает два закрытых целочисленных поля a и b — числитель и знаменатель дроби, приведенной к несократимому виду (для отрицательных дробей знак приписывается к числителю), набор конструкторов и набор стандартных операций: сложение (+), вычитание (−), умножение (*), деление (/) и унарный минус. Кроме того, предусмотреть открытый метод ToStr (без параметров), возвращающий строковое представление дроби в виде «[−]числитель[/знаменатель]»", например, «−2/3» или «4» (если знаменатель равен 1, то он не указывается).

Предусмотреть возможность создания объекта класса FracNum с помощью конструкторов со следующими наборами параметров:

• пустой набор параметров (создается дробь, равная 0);

• два целых числа — числитель и знаменатель дроби в предположении, что знаменатель отличен от нуля (дробь не обязана быть несократимой, знак может указываться для любого параметра);

• одно целое число — числитель дроби (знаменатель полагается равным 1);

• объект типа FracNum.

Предусмотреть также операцию неявного приведения объекта типа FracNum к вещественному типу (это позволит использовать дроби в выражениях, содержащих вещественные данные).

Тестирование разработанного класса. Дана строка Name (имя файла), вещественное число R, целое число K, а также набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа FracNum. Параметры определяют способ создания объекта; каждая группа параметров начинается с символа, после которого указываются дополнительные данные. Возможные варианты параметров:

• символ «0», после которого ничего не указывается (создается нулевая дробь);

• символ «1», после которого указывается целое число — числитель дроби (знаменатель полагается равным 1);

• символ «2», после которого указываются два целых числа — числитель и знаменатель дроби (знаменатель не равен 0);

• символ «f», после которого указывается индекс ранее определенной дроби (коллекция индексируется от 0).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с исходными данными. Каждое из действий выполняет преобразование одной из дробей или вещественного числа R с помощью другой дроби или целого числа K. Вначале указывается символ, определяющий действие («+», «−», «*», «/» или «u» для унарного минуса), затем указывается индекс изменяемой дроби (от 0 до 7) или значение 8, если требуется изменить вещественное число R. Затем для действий «+», «−», «*», «/» указывается второй операнд соответствующей бинарной операции: это либо индекс дроби, либо значение 9 для целого числа K. Для действия «u» (унарный минус) указывается только индекс изменяемой дроби. Вещественное число R всегда изменяется только с помощью некоторой дроби (т. е. вторым операндом в этом случае обязательно будет индекс в диапазоне от 0 до 7). Для операции деления никогда не предлагается второй операнд, соответствующий нулевому числу.

Создать коллекцию из 8 объектов типа FracNum, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел и строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает одну строку файла. В этой строке вначале выводятся два операнда, разделенные знаком операции (или унарный минус и единственный операнд), затем знак равенства и результат выполнения операции. Пробелы не используются, операнды заключаются в круглые скобки. Для вывода дробей используется метод ToStr; вещественные числа выводятся с четырьмя дробными знаками и точкой в качестве разделителя.

Примечание (C++). В C++ для перегрузки операций используются специальные функции с именами operator знак_операции, например, operator+ для операции сложения. Эти функции могут быть членами класса (например, операция + для класса FracNum может быть определена с помощью функции-члена FracNum operator+(const FracNum &f2)) или дружественными функциями (в этом случае операция + для класса FracNum определяется с помощью функции FracNum operator+(const FracNum &f1, const FracNum &f2), причем данная функция определяется в классе FracNum как дружественная: friend FracNum operator+(FracNum&, FracNum&)). Для реализации неявного приведения типов используется функция-член без параметров с именем operator тип, например, operator double() для приведения к типу double.

[C++]

// An auxiliary function to convert a real number
// to a string with 4 fractional digits
// (you do not need to change this function)
string str4(double value)
{
    ostringstream oss;
    oss << fixed << setprecision(4) << value;
    return oss.str() == "-0.0000" ? "0.0000" : oss.str();
}

class FracNum
{
private:
    int a; // numerator
    int b; // denominator

    // An auxiliary function that reduces the numerator
    // and denominator of a fraction to an irreducible form
    void reduce()
    {
        // Implement this function using the algorithm
        // for finding the greatest common divisor (GCD)

    }
public:
    FracNum(int num, int den) : a(num), b(den)
    {
        reduce();
    }

    // Implement other constructors

    operator double()
    {
        return 0;
        // Remove the previous statement and implement
        // the operator for implicit conversion to a double type
    }

    FracNum operator+(const FracNum &f2)
    {
        return FracNum(0, 1);
        // Remove the previous statement
        // and implement the addition operator (+)
    }

    // Implement subtraction (-), multiplication (*), division (/)
    // and unary negation ( FracNum operator-() ) operators

    // Implement the ToStr function
};

Примечание (C#). В C# для перегрузки операций используются специальные статические методы класса с именами operator знак_операции, например, operator+ для операции сложения. Число параметров определяется количеством операндов определяемой операции. Для реализации неявного приведения типов используется статический метод с именем implicit operator тип; его параметром является объект, который требуется привести к данному типу (например, static implicit operator double(FracNum f) для приведения объекта типа FracNum к типу double).

[C#]

public class FracNum
{
    private int a; // numerator
    private int b; // denominator

    // An auxiliary function that reduces the numerator
    // and denominator of a fraction to an irreducible form
    void reduce()
    {
        // Implement this function using the algorithm
        // for finding the greatest common divisor (GCD)

    }
    public FracNum(int num, int den)
    {
        a = num;
        b = den;
        reduce();
    }

    // Implement other constructors

    public static implicit operator double(FracNum f)
    {
        return 0;
        // Remove the previous statement and implement
        // the operator for implicit conversion to a double type
    }
    public static FracNum operator+(FracNum f1, FracNum f2)
    {
        return new FracNum(0, 1);
        // Remove the previous statement and implement
        // the addition operator (+)
    }

    // Implement subtraction (-), multiplication (*), division (/)
    // and unary negation ( FracNum operator-(FracNum f) ) operators

    // Implement the ToStr method
}

// An auxiliary function to convert a real number
// to a string with 4 fractional digits
// (you do not need to change this function)
static string Str4(double value)
{
    string res = value.ToString("F4").Replace(',', '.');
    return res == "-0.0000" ? "0.0000" : res;
}

Примечание (Java). В Java не предусмотрено специальных средств для перегрузки операций (подобных средствам в C++, C# и других языках), поэтому для реализации операций требуется использовать обычные методы с подходящими именами, например: add для операции сложения, sub для операции вычитания, mult для операции умножения, div для операции деления, neg для операции «унарный минус». Кроме того, в Java нельзя определить операции неявного приведения объекта определяемого класса к другому типу, поэтому приходится использовать обычные методы, обеспечивающие такое приведение, например, double toDouble() для приведения объекта к типу double.

[Java]

class FracNum {
    private int a; // numerator
    private int b; // denominator

    // An auxiliary function that reduces the numerator a
    // and denominator b of a fraction to an irreducible form
    private void reduce() {
        // Implement this function using the algorithm
        // for finding the greatest common divisor (GCD)

    }
    public FracNum(int num, int den) {
        a = num;
        b = den;
        reduce();
    }

    // Implement other constructors

    public double toDouble() {
        return 0;
        // Remove the previous statement and implement the method
        // for conversion to a double type
    }

    public FracNum add(FracNum f2) {
        return new FracNum(0, 1);
        // Remove the previous statement and implement the method
        // for addition operator (+)
    }

    // Implement the operators (-), (*), (/) and unary minus
    // as functions sub, mult, div and neg respectively

    // Implement the toStr function
}

public class MyTask extends PT
{
    // An axiliary function to convert a real number
    // to a string with 4 fractional digits
    // (you do not need to change this function)
    static String str4(double value) {
        DecimalFormat df = new DecimalFormat();
        df.setMaximumFractionDigits(4);
        df.setMinimumFractionDigits(4);
        df.setGroupingUsed(false);
        String res = df.format(value).replace(',', '.');
        if (res.equals("-0.0000")) {
            res = "0.0000";
        }
        return res;
    }

Примечание 1 (Python). В Python для перегрузки стандартных операций используются методы со специальными именами. В частности, для операций сложения, вычитания, умножения и обычного (не целочисленного деления) необходимо определить функции с именами __add__, __sub__, __mul__ и __truediv__ соответственно. Все эти функции должны иметь два параметра (self, other), причем self, как обычно, обозначает объект, для которого вызывается эта функция. Операция «унарный минус» может быть переопределена с помощью функции _neg_ с единственным параметром self. Аналогичным образом определяются функции приведения к другим типам; в частности, для реализации приведения к вещественному типу float надо определить функцию __float__ с параметром self. Вместо особой функции ToStr, упоминаемой в задании, в языке Python можно определить функцию __str__ приведения к типу str.

Примечание 2 (Python). Поскольку в языке Python отсутствует возможность перегрузки функций, для определения различных конструкторов следует использовать функцию __init__ с набором параметров, снабженным значениями по умолчанию. Кроме того, можно анализировать тип параметров, используя функцию isinstance (таким способом, в частности, можно реализовать конструктор копирования в дополнение к другим вариантам конструктора).

[Python]

# An auxiliary function to convert a real number
# to a string with 4 fractional digits
# (you do not need to change this function)
def str4(value):
    res = f"{value:0.4f}"
    return res if res != "-0.0000" else "0.0000"

class FracNum:
    # An auxiliary function that reduces the numerator
    # and denominator of a fraction to an irreducible form
    def __reduce(self):
        # Implement this function using the algorithm
        # for finding the greatest common divisor (GCD)
        pass

    def __init__(self, num = 0, den = 1):
        if isinstance(num, FracNum):
            # Implement copying of fields from num object
            # (of type FracNum) to self object
            pass
        else:
            self.__a = num # numerator
            self.__b = den # denominator
            self.__reduce()

    def __float__(self):
        # Implement a function to convert a fraction to float
        pass

    def __str__(self):
        # Implement a function to convert a fraction to string
        pass

    def __add__(self, other):
        # Implement the addition operator (+)
        pass

    # Implement subtraction (-), multiplication (*), division (/)
    # and unary negation as functions __sub__, __mul__,
    # __truediv__ and __neg__ respectively

Примечание 1 (Ruby). В Ruby для перегрузки стандартных операций используются методы, имена которых совпадают с обозначениями операций. В частности, для операций сложения, вычитания, умножения и деления надо определить функции +(other), -(other), *(other) и /(other) соответственно. Для операции «унарный минус» функция должна иметь заголовок def -@. Для функций приведения к другим типам также должны использоваться специальные имена. В частности, для приведения к вещественному типу надо описать функцию to_d, а для приведеня к строковому типу — функцию to_s.

Примечание 2 (Ruby). Как и в языке Python, в языке Ruby можно определить различные варианты конструкторов, используя в функции initialize параметры по умолчанию, а также анализируя фактический тип переданных параметров (например, применяя операцию ===, описанную в примечании к заданию OOP0Begin4).

[Ruby]

# An auxiliary function to convert a real number
# to a string with 4 fractional digits
# (you do not need to change this function)
def str4(value)
    res = sprintf("%.4f", value)
    res != "-0.0000" ? res : "0.0000"
end

class FracNum
    # An auxiliary function that reduces the numerator
    # and denominator of a fraction to an irreducible form
    def reduce
        # Implement this function using the algorithm
        # for finding the greatest common divisor (GCD)
    end

    def initialize(num = 0, den = 1)
        if FracNum === num
            # Implement copying of fields from num object
            # (of type FracNum) to a new object
        else
            @a = num # numerator
            @b = den # denominator
            reduce
        end
    end

    def to_f
        # Implement a method to convert a fraction to float
    end

    def to_s
        # Implement a method to convert a fraction to string
    end

    def +(other)
        # Implement the addition operator (+)
    end

    # Implement operators for subtraction (-), multiplication (*),
    # division (/) and unary negation (-@)

end

Примечание 1 (Julia). В Julia для перегрузки стандартных операций используются функции с именами, совпадающими с этими операциями (например, +, -, *, /). Поскольку стандартные операции реализованы в виде функций из модуля Base, перегруженные варианты этих функций также надо определять, указывая имя модуля, например, Base.:+(f1::FracNum, f2::FracNum) (обратите внимание на дополнительное двоеточие, которое надо указывать в имени функции). Для унарного минуса надо определить функцию Base.:- с одним параметром. Для неявного приведения типов предназначена функция convert(T, x), которая возвращает объект x, преобразованный к типу T. Она также входит в модуль Base; для определения новых вариантов приведения типов надо определить эту функцию с новыми параметрами, например, Base.convert(::Type{Float64}, f::FracNum). Аналогичным образом можно также определить функцию для преобразования нового типа в строку String (вместо функции toStr, которую предлагается разработать в данном задании).

Примечание 2 (Julia). В Julia для определения различных вариантов конструкторов достаточно описать все эти варианты (с различными наборами параметров) при определении структуры (или в виде отдельных функций).

[Julia]

# An auxiliary function to convert a real number
# to a string with 4 fractional digits
# (you do not need to change this function)
using Printf
function str4(value::Float64)
    res = @sprintf("%.4f", value)
    res != "-0.0000" ? res : "0.0000"
end

struct FracNum
    a::Int  # numerator
    b::Int  # denominator

    function FracNum(num::Int, den::Int)
        num, den = simplify(num, den)
        new(num, den)
    end
end

# An auxiliary function that reduces the numerator
# and denominator of a fraction to an irreducible form
function simplify(num::Int, den::Int)
    # Implement this function using the algorithm
    # for finding the greatest common divisor (GCD)
    num, den
end

FracNum(f::FracNum) = FracNum(f.a, f.b)

# Implement other constructors

function Base.convert(::Type{Float64}, f::FracNum)
    0.0
    # Remove the previous statement and implement
    # the operator for implicit conversion to a Float64 type
end

function Base.:+(f1::FracNum, f2::FracNum)
    FracNum(0, 1)
    # Remove the previous statement and implement
    # the addition operator (+)
end

# Implement subtraction (-), multiplication (*), division (/)
# and unary negation Base.:-(f1::FracNum) operators

# Implement the toStr(f::FracNum) function

Примечание (PascalABC.NET). В C# для перегрузки операций используются специальные статические методы класса с именами operator знак_операции, например, function operator+ для операции сложения. Число параметров должно соответствовать количеству операндов определяемой операции. Для реализации неявного приведения типов используется статический метод с заголовком function operator implicit(x: A): B; его параметром является объект типа A, который требуется привести к типу B (например, static function operator implicit(f: FracNum): real для приведения объекта типа FracNum к типу real).

[PascalABC.NET]

type
  FracNum = class
  private
    a: integer;  // числитель
    b: integer;  // знаменатель
    // Вспомогательный метод, который преобразует
    // числитель и знаменатель дроби к несократимому виду
    procedure reduce;
    begin
      // Реализуйте этот метод, используя алгоритм
      // нахождения наибольшего общего делителя (НОД)
    end;
  public
    constructor Create(num, den: integer);
    begin
      a := num;
      b := den;
      reduce;
    end;
    // Реализуйте остальные конструкторы

    static function operator implicit(f: FracNum): real;
    begin
      Result := 0;
      // Удалите предыдущий оператор и реализуйте
      // операцию неявного приведения к типу real
    end;

    static function operator+(f1, f2: FracNum): FracNum;
    begin
      Result := new FracNum(0, 1);
      // Удалите предыдущий оператор и реализуйте операцию сложения (+)
    end;
    // Реализуйте операции вычитания (-), умножения (*), деления (/)
    // и унарного минуса (-, с одним параметром)

    // Реализуйте метод ToStr
  end;

// Вспомогательная функция для преобразования вещественного числа
// в его строковое представление с 4 дробными знаками
function Str4(value: real): string;
begin
  Result := value.ToString('F4');
end;

OOP0Begin8°. Реализовать класс ComplNum, предназначенный для хранения и обработки комплексных чисел. Класс ComplNum включает два закрытых вещественных поля cre и cim — действительную и мнимую часть вещественного числа, набор конструкторов и набор стандартных операций: сложение (+), вычитание (−), умножение (*), деление (/) и унарный минус.

Кроме того, предусмотреть открытый метод ToStr (без параметров), возвращающий строковое представление комплексного числа, содержащее его действительную и мнимую часть, после которой указывается символ «i». Числа выводятся с двумя дробными знаками и точкой в качестве разделителя, пробелы не используются. Примеры: «−2.43+3.21i», «4.00», «−5.25i». Если строковое представление действительной или мнимой части числа имеет вид «0.00», то оно не отображается (за исключением нулевого числа, которое отображается в виде «0.00»).

Описать также открытые методы без параметров Re и Im, возвращающие действительную и мнимую часть комплексного числа.

Предусмотреть возможность создания объекта класса ComplNum с помощью конструкторов со следующими наборами параметров:

• пустой набор параметров (создается число, равное 0);

• одно вещественное число — действительная часть комплексного числа, не имеющего мнимой части;

• два вещественных числа — действительная и мнимая часть комплексного числа;

• объект типа ComplNum.

Тестирование разработанного класса. Дана строка Name (имя файла), вещественное число R, а также набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа ComplNum. Параметры определяют способ создания объекта; каждая группа параметров начинается с символа, после которого указываются дополнительные данные. Возможные варианты параметров:

• символ «0», после которого ничего не указывается (создается нулевое число);

• символ «i», после которого ничего не указывается (создается число, равное мнимой единице i);

• символ «1», после которого указывается вещественное число — действительная часть комплексного числа, не имеющего мнимой части;

• символ «2», после которого указываются два вещественных числа — действительная и мнимая часть комплексного числа;

• символ «c», после которого указывается индекс ранее определенного комплексного числа (коллекция индексируется от 0).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с исходными данными. Каждое из действий выполняет либо преобразование одного из комплексных чисел с помощью другого комплексного числа (или вещественного числа R), либо преобразование вещественного числа путем присваивания ему действительной или мнимой части одного из комплексных чисел. Вначале указывается символ, определяющий действие («+», «−»", «*», «/», «u» для унарного минуса, «r» для нахождения действительной части, «i» для нахождения мнимой части). Затем для действий «+»", «−», «*», «/» указывается индекс изменяемого комплексного числа (от 0 до 7) и второй операнд бинарной операции (индекс комплексного числа или значение 8 для вещественного числа R). Для действия «u» (унарный минус) указывается только индекс изменяемого комплексного числа. Для действий «r» и «i» указывается только индекс комплексного числа, результатом этих действий является запись действительной или мнимой части указанного числа в число R. Для операции деления никогда не предлагается операнд, соответствующий нулевому числу.

Создать коллекцию из 8 объектов типа ComplNum, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел и строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает одну строку файла. В этой строке вначале выводятся операнды, снабженные знаком операции, затем знак равенства и результат операции. Операнды заключаются в круглые скобки, пробелы не используются. Примеры: «(2.00+3.00i)−(1.00+1.00i)=1.00+2.00i», «−(1.00+2.00i)=−1.00−2.00i», «Im(3.00−2.00i)=−2.00». Для вывода комплексных чисел используется метод ToStr; вещественные числа выводятся с двумя дробными знаками и точкой в качестве разделителя.

[C++]

// An auxiliary function to convert a real number
// to a string with 2 fractional digits
// (you do not need to change this function)
string str2(double value)
{
    ostringstream oss;
    oss << fixed << setprecision(2) << value;
    return oss.str() == "-0.00" ? "0.00" : oss.str();
}

class ComplNum
{
private:
    double cre; // real part
    double cim; // imaginary part
public:
    ComplNum(double cre, double cim) : cre(cre), cim(cim) {}

    // Implement other constructors

    ComplNum operator+(const ComplNum &c2)
    {
        return ComplNum(0, 0);
        // Remove the previous statement
        // and implement the addition operator (+)
    }

    // Implement subtraction (-), multiplication (*), division (/)
    // and unary negation ( ComplNum operator-() ) operators

    // Implement the ToStr method
};

[C#]

public class ComplNum
{
    private double cre; // real part
    private double cim; // imaginary part
    public ComplNum(double cre, double cim)
    {
        this.cre = cre;
        this.cim = cim;
    }

    // Implement other constructors

    public static ComplNum operator+(ComplNum c1, ComplNum c2)
    {
        return new ComplNum(0, 0);
        // Remove the previous statement
        // and implement the addition operator (+)
    }

    // Implement subtraction (-), multiplication (*), division (/)
    // and unary negation ( ComplNum operator-(ComplNum c) ) operators

    // Implement the ToStr function
};

// An auxiliary function to convert a real number
// to a string with 2 fractional digits
// (you do not need to change this function)
static string Str2(double value)
{
    string res = value.ToString("F2").Replace(',', '.');
    return res == "-0.00" ? "0.00" : res;
}

[Java]

class ComplNum
{
    private double cre; // real part
    private double cim; // imaginary part
    public ComplNum(double cre, double cim) {
        this.cre = cre;
        this.cim = cim;
    }

    // Implement other constructors

    public ComplNum add(ComplNum c2) {
        return new ComplNum(0, 0);
        // Remove the previous statement and implement
        // the method for addition operator (+)
    }

    // Implement the operators (-), (*), (/) and unary minus
    // as functions sub, mult, div and neg respectively

    // Implement the toStr function
}

public class MyTask extends PT
{
    // An auxiliary function to convert a real number
    // to a string with 2 fractional digits
    // (you do not need to change this function)
    static String str2(double value) {
        DecimalFormat df = new DecimalFormat();
        df.setMaximumFractionDigits(2);
        df.setMinimumFractionDigits(2);
        df.setGroupingUsed(false);
        String res = df.format(value).replace(',','.');
        if (res.equals("-0.00")) {
            res = "0.00";
        }
        return res;
    }

[Python]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
def str2(value):
    res = f"{value:0.2f}"
    return res if res != "-0.00" else "0.00"

class ComplNum:
    def __init__(self, cre = 0.0, cim = 0.0):
        if isinstance(cre, ComplNum):
            # Implement copying of fields from cre object
            # (of type ComplNum) to self object
            pass
        else:
            self.__cre = cre # real part
            self.__cim = cim # imaginary part

    def __add__(self, other):
        # Implement addition of two complex numbers
        pass

    # Implement other operators and the __str__ function
    # to convert a complex number to string

[Ruby]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
def str2(value)
    res = sprintf("%.2f", value)
    res != "-0.00" ? res : "0.00"
end

class ComplNum
    def initialize(cre = 0.0, cim = 0.0)
        if ComplNum === cre
            # Implement copying of fields from cre object
            # (of type ComplNum) to a new object
        else
            @cre = cre # real part
            @cim = cim # imaginary part
        end
    end

    def +(other)
        # Implement the addition operator (+)
    end

    # Implement other operators and the to_s function
    # to convert a complex number to string

end

[Julia]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
using Printf
function str2(value::Float64)
    res = @sprintf("%.2f", value)
    res != "-0.00" ? res : "0.00"
end

struct ComplNum
    cre::Float64  # real part
    cim::Float64  # imaginary part
end

ComplNum() = ComplNum(0.0, 0.0)

# Implement other constructors

function Base.:+(c1::ComplNum, c2::ComplNum)
    ComplNum()
    # Remove the previous statement and implement
    # the addition operator (+)
end

# Implement other operators and the toStr function
# to convert a complex number to string

[PascalABC.NET]

type
  ComplNum = class
  private
    cre: real;  // вещественная часть
    cim: real;  // мнимая часть
  public
    constructor Create(cre, cim: real);
    begin
      self.cre := cre;
      self.cim := cim;
    end;
    // Реализуйте остальные конструкторы

    static function operator+(c1, c2: ComplNum): ComplNum;
    begin
      Result := new ComplNum(0, 0);
      // Удалите предыдущий оператор и реализуйте операцию сложения (+)
    end;
    // Реализуйте операции вычитания (-), умножения (*), деления (/)
    // и унарного минуса (-, с одним параметром)

    // Реализуйте метод ToStr
  end;

// Вспомогательная функция для преобразования вещественного числа
// в его строковое представление с 2 дробными знаками
function Str2(value: real): string;
begin
  Result := value.ToString('F2');
end;

OOP0Begin9°. Глубокое копирование при инициализации и присваивании объектов.

Реализовать класс IntMatrix, предназначенный для хранения и обработки целочисленных матриц произвольного размера. Класс IntMatrix включает следующие закрытые поля: m и n целого типа, содержащие соответственно количество строк и столбцов, data — ссылка на одномерную целочисленную коллекцию (например, массив), в которой содержатся элементы матрицы, расположенные по строкам (вначале располагаются элементы первой строки, затем элементы второй строки и т. д.). Также предусмотреть набор конструкторов и набор открытых методов.

В первую группу методов входят методы без параметров:

• RowCount и ColCount — возвращают число строк и столбцов матрицы соответственно.

Во вторую группу методов входят методы с целочисленными параметрами:

• Row(i) и Col(j) — возвращают новый объект типа IntMatrix, содержащий соответственно i-ю строку и j-й столбец матрицы (строки и столбцы индексируются от 0; если строка или столбец с требуемым индексом отсутствует, то возвращается строка или столбец с нулевыми значениями);

• Val(i, j) — возвращает значение элемента матрицы с индексами i, j (если элемент с указанными индексами отсутствует, то возвращается 0);

• SetVal(i, j, val) — присваивает элементу матрицы с индексами i, j значение val (если элемент с указанными индексами отсутствует, то метод не выполняет никаких действий).

В третью группу методов входят методы с параметром matr2 типа IntMatrix; при их описании предполагается, что они вызываются для матрицы matr1:

• Assign(matr2) — изменяет матрицу matr1, присваивая ей значение матрицы matr2 (при этом копируются поля m и n, а также все элементы коллекции data матрицы matr2 (таким образом, выполняется глубокое копирование данных, которое не сводится к копированию значения ссылки data);

• ConcatRows(matr2) и ConcatCols(matr2) — возвращают матрицу, полученную путем приписывания к матрице matr1 строк или столбцов матрицы matr2 соответственно (строки приписываются снизу, столбцы справа; при необходимости в добавляемых строках и столбцах удаляются лишние элементы или добавляются элементы с нулевыми значениями).

Кроме того, предусмотреть открытый метод ToStr (без параметров), возвращающий строковое представление матрицы в следующем формате: «(элементы строки 0 через пробел|элементы строки 1 через пробел|…)», например: «(2 4)» (вектор-строка), «(4|3|1)» (вектор-столбец), «(1 2|3 4)» — квадратная матрица порядка 2.

Предусмотреть возможность создания объекта класса IntMatrix с помощью конструкторов со следующими наборами параметров (все параметры, кроме параметра matr2, являются целочисленными):

• пустой набор параметров — создается нулевая матрица размера 1 на 1,

• (m, n) — создается нулевая матрица размера m на n;

• (m, n, val) — создается матрица размера m на n, все элементы которой равны val;

• (m, n, val, step) — создается матрица размера m на n, начальный элемент которой равен val, а все последующие элементы, перебираемые по строкам, получаются из предыдущего прибавлением числа step;

• (matr2) — создается копия объекта matr2 (типа IntMatrix); при создании выполняется глубокое копирование коллекции data.

При необходимости предусмотреть деструктор, который освобождает память, выделенную для хранения коллекции data.

Тестирование разработанного класса. Дана строка Name (имя файла) и набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа IntMatrix. Параметры определяют способ создания объекта с помощью соответствующего конструктора; каждая группа параметров начинается с символа, после которого указываются дополнительные данные. Возможные варианты параметров:

• символ «0», после которого ничего не указывается (создается нулевая матрица размера 1 на 1);

• символ «2», после которого указываются два целых числа — параметры m и n;

• символ «3», после которого указываются три целых числа — параметры m, n и val;

• символ «4», после которого указываются четыре целых числа — параметры m, n, val и step;

• символ «m», после которого указывается индекс ранее определенной матрицы (коллекция индексируется от 0).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с исходными данными. Каждое из действий выполняет преобразование одной из исходных матриц. Вначале указывается символ, определяющий действие, затем индекс m1 преобразуемой матрицы (число от 0 до 7), затем дополнительные параметры. Возможные действия:

• = m1 m2 — матрице с индексом m1 присваивается матрица с индексом m2 (используется метод Assign);

• v m1 i1 j1 m2 i2 j2 — элементу матрицы m1 с индексами i1, j1 присваивается значение элемента матрицы m2 с индексами i2, j2 (используются методы Val и SetVal);

• r m1 m2 i — матрице с индексом m1 присваивается i-я строка матрицы с индексом m2 (используются методы Row и Assign);

• c m1 m2 j — матрице с индексом m1 присваивается j-й столбец матрицы с индексом m2 (используются методы Col и Assign);

• | m1 m2 — к матрице с индексом m1 приписываются справа столбцы матрицы с индексом m2 (используются методы ConcatCols и Assign);

• / m1 m2 — к матрице с индексом m1 приписываются снизу строки матрицы с индексом m2 (используются методы ConcatRows и Assign).

Значение m2 (как и m1) всегда является числом от 0 до 7, прочие значения являются неотрицательными целыми числами.

Создать коллекцию из 8 объектов типа IntMatrix, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел, затем строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает четыре строки файла. В первой строке выводятся все данные, связанные с указанным действием (символ и числовые параметры), во второй и третьей строке выводятся исходные матрицы с индексами m1 и m2, в четвертой строке выводится преобразованная матрица (с индексом m1). Данные, выводимые в первой строке, разделяются одним пробелом. Для вывода матриц используется метод ToStr; перед выводом матрицы выводятся два пробела, после вывода каждой матрицы в той же строке выводится пробел и размеры матрицы в квадратных скобках, разделенные символом «x», например: «  (2 4) [1x2]», «  (4|3|1) [3x1]», «  (1 2|3 4) [2x2]».

[C++]

class IntMatrix
{
private:
    int m;     // number of rows
    int n;     // number of columns
    int *data; // array of matrix elements (by rows)
public:
    IntMatrix() : m(1), n(1)
    {
        data = new int[1];
        data[0] = 0;
    }
    ~IntMatrix() // destructor
    {
        delete[] data;
    }

    // Assignment operator with deep copying
    // (you do not need to change this function)
    IntMatrix operator=(const IntMatrix &matr2)
    {
        if (matr2.m * matr2.n != m * n)
        {
            delete[] data;
            data = new int[matr2.m * matr2.n];
        }
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++)
            data[i] = matr2.data[i];
    }

    // Implement other constructors and methods
};

[C#]

public class IntMatrix
{
    private int m;      // number of rows
    private int n;      // number of columns
    private int[] data; // array of matrix elements (by rows)
    public IntMatrix()
    {
        m = 1;
        n = 1;
        data = new int[1];
        data[0] = 0;
    }

    // Assignment with deep copying
    // (you do not need to change this method)
    public void Assign(IntMatrix matr2)
    {
        if (matr2.m * matr2.n != m * n)
            data = new int[matr2.m * matr2.n];
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++)
            data[i] = matr2.data[i];
    }

    // Implement other constructors and methods
}

[Java]

class IntMatrix {
    private int m;      // number of rows
    private int n;      // number of columns
    private int[] data; // array of matrix elements (by rows)
    public IntMatrix() {
        m = 1;
        n = 1;
        data = new int[1];
        data[0] = 0;
    }
    // Assignment with deep copying
    // (you do not need to change this function)
    public void assign(IntMatrix matr2)
    {
        if (matr2.m * matr2.n != m * n) {
            data = new int[matr2.m * matr2.n];
        }
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++) {
            data[i] = matr2.data[i];
        }
    }

    // Implement other constructors and methods
}

public class MyTask extends PT
{

[Python]

class IntMatrix:
    def __init__(self, m = 1, n = 1, val = 0, step = 0):
        if isinstance(m, IntMatrix):
            # Implement deep copying of fields from m object
            # (of type IntMatrix) to self object
            pass
        else:
            self.__m = m                # number of rows
            self.__n = n                # number of columns
            self.__data = (m * n) * [0] # list of matrix elements (by rows)
            for i in range(m*n):
                self.__data[i] = val + i * step

    # Assignment with deep copying
    # (you do not need to change this function)
    def assign(self, matr2):
        if matr2.__m * matr2.__n != self.__m * self.__n:
            self.__data = (matr2.__m * matr2.__n) * [0]
        self.__m = matr2.__m
        self.__n = matr2.__n
        for i in range(self.__m * self.__n):
            self.__data[i] = matr2.__data[i]

    # Implement other methods

[Ruby]

class IntMatrix
    def initialize(m = 1, n = 1, val = 0, step = 0)
        if IntMatrix === m
            # Implement deep copying of fields from m object
            # (of type IntMatrix) to a new object
        else
            @m = m
            @n = n
            @data = Array.new(m * n)
            (0...m * n).each do |i|
                @data[i] = val + i * step
            end
        end
    end

    # Assignment with deep copying
    # (you do not need to change this function)
    def assign(matr2)
        if matr2.m * matr2.n != @m * @n
            @data = Array.new(matr2.m * matr2.n)
        end
        @m = matr2.m
        @n = matr2.n
        (0...@m * @n).each do |i|
            @data[i] = matr2.data[i]
        end
    end

    # Implement other methods

end

[Julia]

mutable struct IntMatrix
    m::Int            # number of rows
    n::Int            # number of columns
    data::Vector{Int} # vector of matrix elements (by rows)
end

IntMatrix() = IntMatrix(1, 1, [0])

# Implement other constructors

# Assignment with deep copying
# (you do not need to change this function)
function assign!(matr1::IntMatrix, matr2::IntMatrix)
    matr1 = deepcopy(matr2)
end

# Implement other functions

[PascalABC.NET]

type
  IntMatrix = class
  private
    m: integer;  // число строк
    n: integer;  // число столбцов
    data: array of integer;  // массив матричных элементов (по строкам)
  public
    constructor Create;
    begin
      m := 1;
      n := 1;
      data := new integer[1];
      data[0] := 0;
    end;

    // Присваивание с глубоким копированием
    // (этот метод не требуется изменять)
    procedure Assign(matr2: IntMatrix);
    begin
      if matr2.m * matr2.n <> m * n then
        data := new integer[matr2.m * matr2.n];
      m := matr2.m;
      n := matr2.n;
      for var i := 0 to m * n - 1 do
        data[i] := matr2.data[i];
    end;

    // Реализуйте остальные конструкторы и методы
  end;

OOP0Begin10°. Дополнить класс IntMatrix, описанный в OOP0Begin9, определив для него набор матричных операций:

• унарный минус,

• сложение матриц одинакового размера (+),

• вычитание матриц одинакового размера (−),

• умножение матрицы на число (*),

• перемножение матриц согласованных размеров (*).

Матрицы A и B допустимо перемножать в указанном порядке, если число столбцов матрицы A равно числу строк матрицы B; умножение выполняется по правилу «строка на столбец»: элемент произведения AB с индексами i, j равен сумме произведений соответствующих элементов строки i матрицы A и столбца j матрицы B.

При попытке выполнить операцию для пары матриц неверного размера должно возбуждаться исключение IntMatrixException c с текстовым сообщением «Wrong sizes for <op>», где в качестве <op> указывается знак операции («+», «−», «*»), например, «Wrong sizes for +».

При выполнении данного задания используются варианты конструкторов класса IntMatrix, описанные в OOP0Begin9, а также методы Assign и ToStr.

Тестирование разработанного класса. Дана строка Name (имя файла), целое число K и набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа IntMatrix. Параметры определяют способ создания объекта; каждая группа параметров начинается с символа, после которого указываются дополнительные данные. В данном задании используются группы параметров, которые начинаются с символов «3», «4» и «m» (см. их описание в OOP0Begin9).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с исходными данными. Каждое из действий выполняет преобразование одной из исходных матриц. Вначале указывается символ, определяющий действие, затем индекс m1 преобразуемой матрицы (число от 0 до 7), затем (для всех команд, кроме «u») дополнительный параметр: индекс матрицы m2 (число от 0 до 7) или число 8. Возможные действия:

• + m1 m2 — к матрице с индексом m1 прибавляется матрица с индексом m2;

• − m1 m2 — из матрицы с индексом m1 вычитается матрица с индексом m2;

• * m1 m2 — матрица с индексом m1 умножается на матрицу с индексом m2;

• * m1 8 — матрица с индексом m1 умножается на целое число K;

• u m1 — к матрице с индексом m1 применяется операция «унарный минус».

Создать коллекцию из 8 объектов типа IntMatrix, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел, затем строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает одну строку файла. В этой строке вначале выводятся два операнда, разделенные знаком операции (или унарный минус и единственный операнд), затем знак равенства и результат выполнения операции. Пробелы не используются. Для вывода матриц используется метод ToStr. Исключения, которые могут возбуждаться при выполнении некоторых матричных операций, должны перехватываться и обрабатываться, при этом текст исключения должен выводиться вместо результата операции (после знака равенства).

[C++]

// An exception class used in matrix operators
// (you do not need to change this class)
class IntMatrixException
{
private:
    string op;
public:
    IntMatrixException(string op) : op(op) {}
    string what() const
    {
        return "Wrong sizes for " + op;
    }
};

class IntMatrix
{
private:
    int m;     // number of rows
    int n;     // number of columns
    int *data; // array of matrix elements (by rows)
public:
    IntMatrix() : m(1), n(1)
    {
        data = new int[1];
        data[0] = 0;
    }
    // Destructor
    ~IntMatrix()
    {
        delete[] data;
    }
    // Assignment operator with deep copying
    // (you do not need to change this function)
    IntMatrix operator=(const IntMatrix &matr2)
    {
        if (matr2.m * matr2.n != m * n)
        {
            delete[] data;
            data = new int[matr2.m * matr2.n];
        }
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++)
            data[i] = matr2.data[i];
    }

    // Implement the required constructors, methods and operators
};

[C#]

// An exception class used in matrix operators
// (you do not need to change this class)
public class IntMatrixException : Exception
{
    public IntMatrixException(string op) : base("Wrong sizes for " + op)
    { }
}

public class IntMatrix
{
    private int m;      // number of rows
    private int n;      // number of columns
    private int[] data; // array of matrix elements (by rows)
    public IntMatrix()
    {
        m = 1;
        n = 1;
        data = new int[1];
        data[0] = 0;
    }

    // Assignment with deep copying
    // (you do not need to change this method)
    public void Assign(IntMatrix matr2)
    {
        if (matr2.m * matr2.n != m * n)
            data = new int[matr2.m * matr2.n];
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++)
            data[i] = matr2.data[i];
    }

    // Implement other constructors, methods and operators
}

[Java]

// An exception class used in matrix operators
// (you do not need to change this class)
class IntMatrixException extends Exception {
    public IntMatrixException(String op) {
        super("Wrong sizes for " + op);
    }
}

class IntMatrix {
    private int m;      // number of rows
    private int n;      // number of columns
    private int[] data; // array of matrix elements (by rows)
    public IntMatrix() {
        m = 1;
        n = 1;
        data = new int[1];
        data[0] = 0;
    }
    // Assignment with deep copying
    // (you do not need to change this function)
    public void assign(IntMatrix matr2)
    {
        if (matr2.m * matr2.n != m * n) {
            data = new int[matr2.m * matr2.n];
        }
        m = matr2.m;
        n = matr2.n;
        for (int i = 0; i < m * n; i++) {
            data[i] = matr2.data[i];
        }
    }

    // Implement other constructors and methods
}

public class MyTask extends PT
{

[Python]

# An exception class used in matrix operators
# (you do not need to change this class)
class IntMatrixException(Exception):
    def __init__(self, op):
        self.__msg = op

    def __str__(self):
        return "Wrong sizes for " + self.__msg

class IntMatrix:
    def __init__(self, m = 1, n = 1, val = 0, step = 0):
        if isinstance(m, IntMatrix):
            # Implement deep copying of fields from m object
            # (of type IntMatrix) to self object
            pass
        else:
            self.__m = m                # number of rows
            self.__n = n                # number of columns
            self.__data = (m * n) * [0] # list of matrix elements (by rows)
            for i in range(m*n):
                self.__data[i] = val + i * step

    # Assignment with deep copying
    # (you do not need to change this function)
    def assign(self, matr2):
        if matr2.__m * matr2.__n != self.__m * self.__n:
            self.__data = (matr2.__m * matr2.__n) * [0]
        self.__m = matr2.__m
        self.__n = matr2.__n
        for i in range(self.__m * self.__n):
            self.__data[i] = matr2.__data[i]

    # Implement other methods

[Ruby]

# An exception class used in matrix operators
# (you do not need to change this class)
class IntMatrixException < StandardError
    def initialize(op)
        @op = op
    end

    def what
        "Wrong sizes for " + @op
    end
end

class IntMatrix
    def initialize(m = 1, n = 1, val = 0, step = 0)
        if IntMatrix === m
            # Implement deep copying of fields from m object
            # (of type IntMatrix) to a new object
        else
            @m = m
            @n = n
            @data = Array.new(m * n)
            (0...m * n).each do |i|
                @data[i] = val + i * step
            end
        end
    end

    # Assignment with deep copying
    # (you do not need to change this function)
    def assign(matr2)
        if matr2.m * matr2.n != @m * @n
            @data = Array.new(matr2.m * matr2.n)
        end
        @m = matr2.m
        @n = matr2.n
        (0...@m * @n).each do |i|
            @data[i] = matr2.data[i]
        end
    end

    # Implement other methods

end

[Julia]

# An exception struct used in matrix operators
# and the associated message function
# (you do not need to change this struct and function)
struct IntMatrixException <: Exception
    op::String
end
function message(e::IntMatrixException)
    return "Wrong sizes for " * e.op
end

mutable struct IntMatrix
    m::Int            # number of rows
    n::Int            # number of columns
    data::Vector{Int} # vector of matrix elements (by rows)
end

IntMatrix() = IntMatrix(1, 1, [0])

# Implement other constructors

# Assignment with deep copying
# (you do not need to change this function)
function assign!(matr1::IntMatrix, matr2::IntMatrix)
    matr1 = deepcopy(matr2)
end

# Implement other functions

[PascalABC.NET]

type
  // Класс-исключение, используемый в матричных операциях
  IntMatrixException = class(Exception)
  public
    constructor Create(op: string);
    begin
      inherited Create('Wrong sizes for ' + op);
    end;
  end;

  IntMatrix = class
  private
    m: integer;  // число строк
    n: integer;  // число столбцов
    data: array of integer;  // массив матричных элементов (по строкам)
  public
    constructor Create;
    begin
      m := 1;
      n := 1;
      data := new integer[1];
      data[0] := 0;
    end;

    // Присваивание с глубоким копированием
    // (этот метод не требуется изменять)
    procedure Assign(matr2: IntMatrix);
    begin
      if matr2.m * matr2.n <> m * n then
        data := new integer[matr2.m * matr2.n];
      m := matr2.m;
      n := matr2.n;
      for var i := 0 to m * n - 1 do
        data[i] := matr2.data[i];
    end;

    // Реализуйте остальные конструкторы, методы и операции
  end;

OOP0Begin11°. Реализовать класс IntPolynom, предназначенный для хранения и обработки полиномов произвольной степени с целочисленными коэффициентами. Набор закрытых полей для хранения полиномов разработать самостоятельно.

Предусмотреть следующий набор открытых методов:

• p1.Deg (без параметров) — возвращает степень полинома p1 (целое число); степень нулевого полинома полагается равной −1;

• p1.Val(x) — возвращает значение полинома p1 для вещественного аргумента x (вещественное число);

• p1.Assign(p2) — изменяет полином p1, присваивая ему данные полинома p2 (в этом методе может потребоваться выполнять глубокое копирование данных);

• p1.ToStr (без параметров) — возвращает строковое представление полинома в следующем формате: «c0x^d+c1x^(d-1)+…+cd-1x+cd», например: «4x^3+x^2−32x+1». Слагаемые с нулевыми коэффициентами не указываются (за исключением нулевого полинома); не указываются также коэффициенты, равные 1 и −1. Строковое представление не содержит пробелов. Для нулевого полинома возвращается строка «0».

Также определить набор операций для класса IntPolynom: бинарные операции «+», «−», «*» — соответственно сложение, вычитание и умножение полиномов, а также операцию унарный минус. Следует учесть, что степень суммы или разности полиномов может быть меньше, чем степень любого из операндов.

Предусмотреть возможность создания объекта класса IntPolynom с помощью конструкторов со следующими наборами параметров (все параметры, кроме параметра p2, являются целочисленными):

• пустой набор параметров — создается нулевой полином P(x) = 0;

• (c0) — если число c0 не равно 0, то создается полином степени 0 со значением c0: P(x) = c0, иначе создается нулевой полином;

• (c0, d) — если число c0 не равно 0, то создается полином степени d с коэффициентом c0 при старшей степени (остальные коэффициенты являются нулевыми): P(x) = c0·xd, иначе создается нулевой полином;

• (p2) — создается копия объекта p2 типа IntPolynom (в этом варианте конструктора может потребоваться выполнять глубокое копирование данных).

При необходимости предусмотреть деструктор, который освобождает память, выделенную в объекте типа IntPolynom для хранения данных.

Тестирование разработанного класса. Дана строка Name (имя файла), целое число K, вещественное число X и набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа IntPolynom. Параметры определяют способ создания объекта; каждая группа параметров начинается с символа, после которого указываются дополнительные данные. Возможные варианты параметров:

• символ «0», после которого ничего не указывается (создается нулевой полином);

• символ «1», после которого указывается целое число — параметр c0;

• символ «2», после которого указываются два целых числа — параметры c0 и d;

• символ «p», после которого указывается индекс ранее определенного полинома (коллекция индексируется от 0).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с исходными данными. Каждое из действий выполняет преобразование одного из исходных полиномов или вычисляет его значение для аргумента X. Вначале указывается символ, определяющий действие, затем индекс p1 преобразуемого или вычисляемого полинома (число от 0 до 7), затем дополнительные параметры. Возможные действия: x p1 — вычисляется значение полинома с индексом p1 для аргумента X (используется метод Val); u p1 — к полиному с индексом p1 применяется операция «унарный минус».

Также определены действия для бинарных операций «+», «−», «*». Для них после символа операции указывается индекс изменяемого полинома p1 (число от 0 до 7), а затем — число p2, определяющее второй операнд: если p2 лежит в диапазоне от 0 до 7, то вторым операндом является полином с индексом p2, если p2 = 8, то вторым операндом является целое число K.

Создать коллекцию из 8 объектов типа IntPolynom, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел и строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает одну строку файла. Для вывода полиномов используется метод ToStr. Для действия «x» указывается полином с индексом p1 (в круглых скобках), число X (также в круглых скобках), знак равенства и значение P(X) полинома для аргумента X. Вещественные числа X и P(X) выводятся с двумя дробными знаками и точкой в качестве разделителя. Например: «(x^2+1)(2.00)=5.00». Для остальных действий (операций над полиномами) вначале выводятся два операнда, разделенные знаком операции (или унарный минус и единственный операнд); операнды заключаются в круглые скобки. Затем указывается знак равенства, результат выполнения операции (новое значение полинома с индексом p1), пробел и степень полученного полинома в квадратных скобках. Например: «(x^2+3)−(2x^3+2x^2)=−2x^3-x^2+3 [3]».

[C++]

// An auxiliary function to convert a real number
// to a string with 2 fractional digits
// (you do not need to change this function)
string str2(double value)
{
    ostringstream oss;
    oss << fixed << setprecision(2) << value;
    return oss.str() == "-0.00" ? "0.00" : oss.str();
}

class IntPolynom
{
private:
    // Add the required fields

public:
    // Implement the required constructors, methods and operators

};

[C#]

public class IntPolynom
{
    // Add the required fields

    // Implement the required constructors, methods and operators

}

// An auxiliary function to convert a real number
// to a string with 2 fractional digits
// (you do not need to change this function)
static string Str2(double value)
{
    string res = value.ToString("F2").Replace(',', '.');
    return res == "-0.00" ? "0.00" : res;
}

[Java]

class IntPolynom
{
    // Add the required fields

    // Implement the required constructors and methods

}

public class MyTask extends PT
{
    // An auxiliary function to convert a real number
    // to a string with 2 fractional digits
    // (you do not need to change this function)
    static String str2(double value) {
        DecimalFormat df = new DecimalFormat();
        df.setMaximumFractionDigits(2);
        df.setMinimumFractionDigits(2);
        df.setGroupingUsed(false);
        String res = df.format(value).replace(',','.');
        if (res.equals("-0.00")) {
            res = "0.00";
        }
        return res;
    }

[Python]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
def str2(value):
    res = f"{value:0.2f}"
    return res if res != "-0.00" else "0.00"

class IntPolynom:
    # Implement the required constructors and methods
    pass

[Ruby]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
def str2(value)
    res = sprintf("%.2f", value)
    res != "-0.00" ? res : "0.00"
end

class IntPolynom
    # Implement the required constructors and methods
end

[Julia]

# An auxiliary function to convert a real number
# to a string with 2 fractional digits
# (you do not need to change this function)
using Printf
function str2(value::Float64)
    res = @sprintf("%.2f", value)
    res != "-0.00" ? res : "0.00"
end

mutable struct IntPolynom
    # Add the required fields
end

# Implement the required constructors, functions and operators

[PascalABC.NET]

type
  IntPolynom = class
  private
    // Добавьте требуемые поля
  public
    // Реализуйте требуемые конструкторы, методы и операции
  end;

// Вспомогательная функция для преобразования вещественного числа
// в его строковое представление с 2 дробными знаками
function Str2(value: real): string;
begin
  Result := value.ToString('F2');
end;

OOP0Begin12°. Реализовать класс LInt, предназначенный для хранения и обработки целых чисел произвольной длины («длинных чисел»). Набор закрытых полей для хранения длинных чисел разработать самостоятельно.

Класс LInt включает набор конструкторов и набор арифметических операций: сложение (+), вычитание (−), умножение (*) и унарный минус. Он также содержит следующие открытые методы:

• n1.Assign(n2) — изменяет длинное число n1, присваивая ему значение длинного числа n2 (в этом методе может потребоваться выполнять глубокое копирование данных);

• n1.ToStr (без параметров) — возвращает строковое представление длинного числа n1.

Предусмотреть возможность создания объекта класса LInt с помощью конструкторов со следующими наборами параметров:

• пустой набор параметров — создается нулевое длинное число;

• целочисленный параметр num — создается длинное число, равное обычному целому числу num;

• строковый параметр str — создается длинное число, строковое представление которого определяется строкой str (можно считать, что строка str представляет правильное число, т. е. не является пустой и содержит только цифры и, возможно, начальный символ «−»);

• параметр n2 типа LInt — создается копия объекта n2 (в этом варианте конструктора может потребоваться выполнять глубокое копирование данных).

Также определить открытый статический метод Power10(p), который возвращает длинное число, равное 10 в степени p (p — неотрицательное целое число).

При необходимости предусмотреть деструктор, который освобождает память, выделенную в объекте типа LInt для хранения данных.

Тестирование разработанного класса. Дана строка Name (имя файла), целое число K и набор из 8 групп параметров, позволяющих создать коллекцию (например, массив) из 8 объектов типа LInt. Параметры определяют способ создания объекта; каждая группа начинается с символа, после которого указываются дополнительные данные. Возможные варианты параметров:

• символ «0», после которого ничего не указывается (создается нулевое длинное число);

• символ «n», после которого указывается целое число num (используется конструктор с параметром num);

• символ «s», после которого указывается строка str (используется конструктор с параметром str);

• символ «p», после которого указывается целое число d (используется функция Power10 с параметром d);

• символ «i», после которого указывается индекс ранее определенного длинного числа (создается число, равное этому длинному числу).

Кроме того, дано целое число N и набор из N групп параметров, определяющих действия, связанные с изменением исходных длинных чисел. Каждое из действий выполняет преобразование одного из длинных чисел с помощью другого длинного числа или обычного целого числа K. Вначале указывается символ, определяющий действие («+», «−», «*» или «u» для унарного минуса), затем указывается индекс изменяемого длинного числа (от 0 до 7). Затем для действий «+»", «−», «*» указывается второй операнд соответствующей бинарной операции: это либо индекс длинного числа (от 0 до 7), либо значение 8 для целого числа K (значение 8 может указываться только для действия «*»). Для действия «u» (унарный минус) указывается только индекс изменяемого длинного числа.

Создать коллекцию из 8 объектов типа LInt, инициализировав их требуемым образом, и выполнить для данных объектов все указанные действия.

В текстовый файл с именем Name записывается информация, связанная с созданием объектов и выполнением требуемых действий. Вначале в файл выводятся строковые представления всех созданных объектов, полученные с помощью метода ToStr (каждое представление отображается на отдельной строке; вначале выводится индекс объекта, затем пробел, затем строковое представление объекта). Затем в файл заносится информация о выполненных действиях.

Информация о каждом действии занимает одну строку файла. В этой строке вначале выводятся два операнда, разделенные знаком операции (или унарный минус и единственный операнд), затем знак равенства и результат выполнения операции. Пробелы не используются; операнды, заключаются в круглые скобки. Для вывода длинных чисел используется метод ToStr.

[C++]

class LInt
{
private:
    // Add the required fields

public:
    // Implement the required constructors, methods and operators

};

[C#]

public class LInt
{
    // Add the required fields

    // Implement the required constructors, methods and operators

}

[Java]

class LInt
{
    // Add the required fields

    // Implement the required constructors and methods

}

public class MyTask extends PT
{

[Python]

class LInt:
    # Implement the required constructors and methods
    pass

[Ruby]

class LInt
    # Implement the required constructors and methods
end

[Julia]

mutable struct LInt
    # Add the required fields
end

# Implement the required constructors, functions and operators

[PascalABC.NET]

type
  LInt = class
  private
    // Добавьте требуемые поля
  public
    // Реализуйте требуемые конструкторы, методы и операции
  end;

PrevNext

 

Рейтинг@Mail.ru

Разработка сайта:
М. Э. Абрамян, В. Н. Брагилевский

Последнее обновление:
01.01.2025