Втора задача
- Краен срок:
- 31.10.2012 17:00
- Точки:
- 6
Срокът за предаване на решения е отминал
Имплементирайте класове за работа с музикална колекция. Те трябва да могат да прочитат текстов файл с определен формат и да предлагат определена функционалност за филтриране.
Формат
Текстовият файл, дефиниращ музикална колекция, е подобен на информацията, която MTV предлагаше през 90те:
Fields of Gold
Sting
Ten Summoner's Tales
Mad About You
Sting
The Soul Cages
Fields of Gold
Eva Cassidy
Live at Blues Alley
Autumn Leaves
Eva Cassidy
Live at Blues Alley
Autumn Leaves
Bill Evans
Portrait in Jazz
Brain of J.F.K
Pearl Jam
Yield
Jeremy
Pearl Jam
Ten
Come Away With Me
Norah Johnes
One
Acknowledgment
John Coltrane
A Love Supreme
Ruby, My Dear
Thelonious Monk
Mysterioso
Всяка песен е блок от три реда. Първият ред е името на песента, вторият ред е изпълнителят, а третият - албумът. Песните са разделени с един празен ред. Приемете, че входът винаги е правилно форматиран и (очевидно) няма символ за нов ред в имената на песни, изпълнители или албуми.
Създаването на колекция трябва да става така:
collection = Collection.parse(text)
Където text
е текстов низ със съдържание във формата, описан по-горе (с цел опростена проверка).
Операции с колекция
Всяка колекция трябва да има три метода - artists
, albums
и names
- които да връщат масив от низове, съдържащи съответно имена на изпълнители, албуми или песни. Във върнатия списък не трябва да има повторения. Редът трябва да е същият като подадения в text
.
Отделно, колекциите трябва да имплементират Enumerable
. Всяка песен от колекцията трябва да е обект и с (поне) три метода - name
, artist
и album
, които връщат съответната информация (като низ). Редът трябва да бъде същият като подадения в text
.
Филтриране на колекции
Колекцията трябва да има метод filter
, който да приема критерии за филтриране. Възможни критерии са:
Criteria.name('Fields of Gold') # Име на песен
Criteria.artist('Sting') # Име на изпълнител
Criteria.album("Ten Summoner's Tales") # Име на албум
Criteria
е някакъв обект/модул/клас, който има съответните три метода. Всеки от тях връща някакъв обект, представляващ критерий, по който да може да се филтрира колекция, подавайки го като аргумент на Collection#filter
. Например, ако искате да намерите всички песни на Jeff Buckley, може да го направите така:
collection.filter Criteria.artist('Jeff Buckley')
filter
връща подмножество на колекцията. Няма значение от какъв клас е, стига да има същите методи като колекцията.
Отделно, критериите могат да се комбинират с &
, |
, !
, където първото е конюнкция, а второто - дизюнкция. Така може да намерите (1) всички песни на Eva Cassidy и Norah Johnes или (2) всички песни на Sting, които не са Fields of Gold:
collection.filter Criteria.artist('Eva Cassidy') | Criteria.artist('Norah Johnes')
collection.filter Criteria.artist('Sting') & !Criteria.name('Fields of Gold')
Обединяване на подмножества
Колекциите и подмножествата, връщани от filter
, трябва да имат допълнително и метод adjoin
, който да може да обедини две подмножества. Например, ако искате да вземете всички песни на Eva Cassidy и Norah Johnes, може да го направите по два начина:
collection.filter Criteria.artist('Norah Johnes') | Criteria.artist('Eva Cassidy')
collection.filter(Criteria.artist('Norah Johnes')).adjoin(collection.filter(Criteria.artist('Eva Cassidy')))
При такова обединение на подмножества/колекции, редът на песните в резултата не е дефиниран.
Бележки
- Имаме следните изисквания към константи и интерфейс:
-
Collection
с "класов" методparse
и методиartists
,albums
,names
,filter
иadjoin
. Последните два връщат нещо, което има същите методи. -
Criteria
с "класови" методиname
,artist
иalbum
, които връщат обекти, имплементиращи оператори&
,|
и!
. Въпросните обекти могат да бъдат подавани наfilter
. - Колекцията, върната от
Collection.parse
, да е enumerable и да може да се третира като стандаретн обект от този тип. Това важи и за обектите тип "подмножество на колекция". - Елементите (обектите песни), които колекцията подава при обхождане, да имат методи
name
,artist
иalbum
.
-
- Обърнете внимание, че:
- Не ни интересува типът на върнатото от
Collection.parse
. - Не ни интересува дали "колекция" и "подмножество на колекция" са един и същи или два различни класа, стига и двата да отговарят на един и същ интерфейс (да могат да бъдат обхождани).
- Аналогично, не ни интересува типът на обекта, връщан от
Criteria
, стига да отговаря на съответните условия. - Не ни интересува типът на песните, а само интерфейсът към тях.
- Свободни сте да вземете всички тези решения както намирате за уместно.
- Не ни интересува типът на върнатото от
- Ако ползвате
eval
или нещо сродно, ще ви взимаме точки. - Редът на песните винаги е като този във файла. Изключение прави само резултатът от
adjoin
. - Възникват интересни въпроси за това кога две песни са равни. За простота, приемете че
adjoin
се прави само между подмножества на една и съща колекция. Възможно е да се прави и между колекция и нейно подмножество. Няма да се прави между подмножества от две различни колекции, обаче. Т.е., не е дефинирано какво правиCollection.parse(text).adjoin(Collection.parse(text))
. -
filter
иadjoin
връщат нов обект, а не променят стария.
Примерен тест
Можете да намерите примерен тест и инструкции как да го пуснете в хранилището с домашните в GitHub.
Ограничения
Тази задача има следните ограничения:
- Най-много 80 символа на ред
- Най-много 4 реда на метод
- Най-много 1 нива на влагане
Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.
Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop
редовно, докато пишете кода. Ако смятате, че rubocop
греши по някакъв начин,
пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като
private gist. Ако пуснете кода си публично (например във форумите), ще смятаме
това за преписване.