Трета задача

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

Краен срок:
26.10.2015 17:30
Точки:
6

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

Математически безполезности

Когато ги тресе липсата на вдъхновение, преподавателите по програмиране много обичат да дават задачи с някаква "математика" в тях - факториели, фибоначита и прочее. Когато липсата мине на стероиди, резултатът е тази задача.

В нея ще упражните употребата и имплементацията на класове, миксиращи Enumerable. Огледайте внимателно документацията на модула, понеже има методи, които ще ви бъдат полезни.

Три поредици

Дефинирайте класове за следните три поредици.

RationalSequence

Знаете ли, че между рационалните числа и естествените числа може да се построи биекция? С други думи (и много неточност), има "толкова" естествени числа, "колкото" и рационални (реалните, за сметка на това, са "повече"). Първата поредица ще ви помогне да осмислите как става.

sequence = RationalSequence.new(4)
sequence.to_a # => [(1/1), (2/1), (1/2), (1/3)]

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

Обърнете внимание, че всяко рационално число присъства само веднъж - 2/2 е същото като 1/1 и се изпуска. За целите на тази задача, ще приемем, че всички рационални числа са подредени така.

(За да сме съвсем точни, ако искаме да изброим всички рационални числа, можем да започнем от нула и да редуваме знаци - [0, (1/1), -(1/1), (2/1), -(2/1)], но това не ни интересува в тази задача.)

PrimeSequence

Създайте подобна поредица, само за прости числа:

sequence = PrimeSequence.new(5)
sequence.to_a # => [2, 3, 5, 7, 11]

FibonacciSequence

И понеже няма как да се мине без Фибоначи, създайте и една последователност за него:

sequence = FibonacciSequence.new(5)
sequence.to_a # => [1, 1, 2, 3, 5]

Добавете възможност първите два аргумента в редицата на Фибоначи да се подават като аргументи с ключови думи.

sequence = FibonacciSequence.new(5, first: 0, second: 1)
sequence.to_a # => [0, 1, 1, 2, 3]

Въпреки, че е синтактично възможно, няма да тестваме какво става, ако е подаден само един от двата аргумента first или second. Това е недефинирано поведение, което може да имплементирате както искате, или да не се грижите за това изобщо.

Три безполезни функции

Нека да използваме тези поредици, за да дефинираме няколко функции.

В Ruby не е добра практика да се дефинират top-level методи. Вместо това, хубаво е всеки дефиниран метод да се постави в клас или модул. Създайте един такъв модул, който, поради липса на смисъл, кръстете DrunkenMathematician. Ето как може да се дефинира:

module DrunkenMathematician
  module_function

  def answer
    42
  end
end

DrunkenMathematician.answer # => 42

Извикването на module_function позволява всички функции, дефинирани в модула, да могат да се извикват директно върху самия модул (подобно на статични функции в C++ и Java), без да има нужда да миксираме модула в клас и да инстанцираме класа.

Функциите, които ще трябва да дефинирате в него, са:

meaningless

Приема един аргумент n. Функцията взима първите n рационални числа и ги разделя на две групи:

  • група 1, съдържаща рационални числа, чиито числител или знаменател е прост
  • група 2, която съдържа такива рационални числа, на които нито числителят, нито знаменателят са прости

Функцията връща произведението на първата група, разделено на произведението на втората група.

# първите шест рационални числа: 1/1, 2/1, 1/2, 1/3, 3/1, 4/1
# група 1:                       2/1, 1/2, 1/3, 3/1
# група 2:                       1/1, 4/1

DrunkenMathematician.meaningless(6) # => (1/4)

aimless

Приема един аргумент n. Функцията:

  • взема първите n прости числа
  • разделя ги на двойки
  • прави рационално число от всяка двойка, като ползва първото за числител и второто за знаменател
  • ако n е нечетно, знаменателят на последното число е 1
  • връща сумата на тези числа
# първите четири числа са: 2, 3, 5, 7
# обърнати до рационални:  2/3, 5/7
# сумата им:               29/21

DrunkenMathematician.aimless(4) # => (29/21)

worthless

Нека наречем последователността от първите x рационални числа "отрязък". Така например, RationalSequence.new(4) ни връща отрязък с първите 4 рационални числа, а RationalSequence.new(10) - с първите 10.

Напишете функция DrunkenMathematician.worthless(n), приемаща един аргумент n. Тя трябва да връща масив, съдържащ най-големия отрязък на рационалните числа, сумата на които не надвишава n-тото число на Фибоначи.

DrunkenMathematician.worthless(5) # => [(1/1), (2/1), (1/2), (1/3)]

Разни уточнения

  • Направете всичко да работи добре с аргумент 0. Множеството на първите 0 рационални числа е празното множество. Произведението на 0 числа е 1. Сумата на 0 числа е 0.
  • Ако си добавите #prime? метод в числата, решението може да стане по-простичко. Вие преценете в кой клас/модул да го добавите. Това е единственият monkey patch, който ви разрешаваме.
  • Никой от *Sequence класовете не трябва да изчислява резултатите си предварително. Например, RationalSequence.new(10_000) не трябва да конструира масив с 10 000 елемента – вместо това трябва да ги генерира един по един.
  • Всички *Sequence класове трябва да миксират Enumerable.
  • Тази задача има няколко възможни решения. Вие преценете кое да използвате.

Ограничения

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

  • Най-много 100 символа на ред
  • Най-много 15 реда на метод
  • Най-много 2 нива на влагане

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

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