Втора задача

Предадени решения

Краен срок:
27.10.2014 17:00
Точки:
6

Срокът за предаване на решения е отминал

NumberSet

В тази задача искаме от вас да създадете обект, който може да държи в себе си множество числа от различни типове. За целта, ще трябва да напишете клас NumberSet, който да поддържа следните операции:

Добавяне на число

Добавяне на числа в обект от клас NumberSet става чрез използване на метода <<:

numbers = NumberSet.new
numbers << 6
numbers << 7.6
numbers << Rational(22, 7)
numbers << 2.5+3i

Както виждате, един NumberSet може да съдържа цели числа, числа с плаваща запетая, рационални и комплексни числа. Подробност при добавянето на числа е, че NumberSet обект трябва да съдържа само уникални елементи. Тоест, можем да добавим число само веднъж в даден NumberSet:

numbers = NumberSet.new
numbers << 42
numbers << 42
numbers.size  #=> 1

За еднакви числа ще считаме и такива от различни типове, но с равна стойност. Например:

  • 3 и 3.0 се считат за едно и също число
  • Rational(22, 2) и 11 се считат за едно и също число
  • 2.5 и 2.5+0i се считат за едно и също число

Тоест, ако сте добавили числото 42 в множеството, опитът за добавяне на 42, Rational(84, 2), 42+0i и подобни ще е неуспешен. Те няма да заместят първоначално добавената стойност.

Проверяване на размер

Обектите NumberSet трябва да предоставят и методи size и empty?, които трябва да работят както очаквате, с малкото уточнение за уникалните числа по-горе.

  • size връща броя на елементите, съдържани от NumberSet обекта.
  • empty? е предикат, който връща false, ако има числа в NumberSet обекта и true в противен случай.

Филтриране

Класът трябва да предоставя и метода [], който ще има специално значение в контекста на NumberSet. Този метод приема един аргумент и го използва като филтър, за да селектира елементи от множеството, задоволяващи условието на филтъра и след това връща тези елементи под формата на нов NumberSet обект.

Има три типа филтри:

  • Filter - приема блок в конструктора. Блокът от своя страна приема като аргумент число и връща true или false, в зависимост от това дали числото задоволява филтъра. Например, този филтър допуска само четни числа: Filter.new { |number| number.even? }.
  • TypeFilter - приема в конструктора си единствен аргумент, чиято стойност може да бъде :integer, :real или :complex. Ако подаденият аргумент има стойност :integer, филтърът оставя всички числа от тип Integer. Ако подаденият аргумент има стойност :real, филтърът оставя всички числа от тип Float или Rational. Ако подаденият аргумент има стойност :complex, филтърът оставя всички числа от тип Complex.
  • SignFilter - приема в конструктора си единствен аргумент, чиято стойност може да бъде :positive, :non_positive, :negative или :non_negative като съответно филтърът отсява положителни (n > 0), неположителни (n <= 0), отрицателни (n < 0) и неотрицателни числа (n >= 0). Този филтър не е дефиниран за комплексни числа и затова няма да тестваме такива с него.

Филтрите трябва да поддържат два специални оператора - & и |.

  • & е оператор за сечение на два филтъра. Резултатът е трети филтър, който е удовлетворен от число, което удовлетворява и двата филтъра.
  • | е оператор за обединение на два филтъра. Резултатът е трети филтър, който е удовлетворен от число, което удовлетворява поне един от двата филтъра.

Пример за филтриране на числа:

numbers = NumberSet.new
[-3, -2, -1, 0, 1, 2, 3, 4, 5].each do |number|
  numbers << number
end
numbers[SignFilter.new(:non_negative) & Filter.new { |number| number.even? }].to_a #=> [0, 2, 4]

Ограничения

Ще тестваме NumberSet само с числа от типовете Fixnum, Float, Rational и Complex. Няма да подаваме грешни стойности на филтрите.

Enumerable

NumberSet трябва да имплементира Enumerable, позволявайки ни да итерираме по числата, които са част от множеството, и да ни дава възможност да използваме всички удобни методи, предоставени от модула.

Това означава, че вашият клас трябва да има в началото си include Enumerable и да дефинира метод each, който реализира обхождане.

Примерен тест

Примерния тест може да намерите примерния тест в хранилището с домашните.

Ограничения

Тази задача има следните ограничения:

  • Най-много 80 символа на ред
  • Най-много 8 реда на метод
  • Най-много 2 нива на влагане
  • Най-много 4 аргумента на метод

Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.

Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop редовно, докато пишете кода. Ако смятате, че rubocop греши по някакъв начин, пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като private gist. Ако пуснете кода си публично (например във форумите), ще смятаме това за преписване.