Георги обнови решението на 30.10.2011 21:18 (преди около 13 години)
+require 'set'
+
+class Song
+ attr_accessor :name, :artist, :genre, :subgenre, :tags
+
+ def initialize(name, artist, genre, subgenre=nil, tags=[])
+ @name, @artist, @genre, @subgenre = name, artist, genre, subgenre
+ @tags = Set.new(tags)
+ @tags << @genre.downcase
+ @tags << @subgenre.downcase if @subgenre
+ end
+
+ def matches? criteria
+ predicate_results = criteria.map { |criterion, value| \
+ check_criterion criterion, value }
+ predicate_results.inject(true) { |result, element| result and element}
+ end
+
+ private
+
+ def match_tags? tags
+ tags = [tags] unless tags.kind_of? Enumerable
+ tags = Set.new(tags.map(&:downcase))
+ negative = tags.select { |tag| tag.end_with? '!' }
+ positive = tags - negative
+ negative = negative.map {|tag| tag.chop }
+ contains_all_positive = @tags.intersection(positive) == positive
+ contains_no_negative = @tags.intersection(negative).empty?
+ contains_all_positive and contains_no_negative
+ end
+
+ def check_criterion criterion, value
+ case criterion
+ when :tags then match_tags? value
+ when :name, :artist then self.send(criterion) == value
+ when :filter then value.(self)
+ else true
+ end
+ end
+end
+
+class Collection
+ include Enumerable
+
+ def initialize(song_as_string, tags={})
+ @songs = Set.new
+ song_as_string.lines.each do |line|
+ name, artist, genre_string, tags_string = split line, /\./
+ genre, subgenre = split genre_string, /,/
+ tag_set = Set.new
+ tag_set += split(tags_string, /,/).to_set if tags_string
+ tag_set += tags.fetch artist, Set.new
+ tag_set = tag_set.map(&:downcase)
+ @songs << Song.new(name, artist, genre, subgenre, tag_set)
+ end
+ end
+
+ def find criteria={}
+ self.select { |song| song.matches? criteria }
+ end
+
+ def each
+ @songs.each { |song| yield song }
+ end
+
+ private
+
+ def split string, regex
+ string.split(regex).map(&:strip) if string
+ end
+end
+
- Добре подредено. Харесва ми.
- В
matches?
нарушаваш конвенция. Ако блокът ти не е на същия ред, трябва да ползвашdo/end
, не{ }
. - Отделно, напълно излишно е да екранираш новия ред с
\
. - В
Song#initialize
си искал да ползвашsong_as_string.map
. - Не мога да разбера нуждата от
Collection#split
. Гледайки кода ти, не виждам къде го викаш сstring = nil
. Съответно, не виждам смисъл вif
-а. - Не ми харесва
when :name, :artist then send(criterion)
. По-добре щеше да е да направиш два отделни блока вcase
-а. Щеше да се разбира по-лесно. Също така, нямаше да бъде повторение. Важното е да минимизираш повторение на знание, не повторение на символи. -
self.
е излишен на двете места, на които си го написал.
Ще ти дам една бонус точка. Щяха да са две, но онова нарушаване на конвенцията ме спира :)