Илия обнови решението на 31.10.2012 02:28 (преди около 12 години)
+class Song
+ attr_accessor :name, :artist, :album
+
+ def initialize(song_text)
+ @name, @artist, @album = song_text.split("\n")
+ end
+
+ def value_for(key)
+ public_send key
+ end
+end
+
+class Collection
+ include Enumerable
+ attr_accessor :songs
+
+ def Collection.make(array)
+ Collection.new.tap { |collection| collection.songs = array }
+ end
+
+ def Collection.parse(text)
+ Collection.make text.split("\n\n").map { |info| Song.new(info) }
+ end
+
+ def each
+ @songs.each { |item| yield item }
+ end
+
+ def artists
+ map(&:artist).uniq
+ end
+
+ def albums
+ map(&:album).uniq
+ end
+
+ def names
+ map(&:name).uniq
+ end
+
+ def filter(criteria)
+ Collection.make select { |song| criteria.fulfilled? song }
+ end
+
+ def adjoin other_collection
+ Collection.make self.songs |= other_collection.songs
+ end
+end
+
+class Criteria
+ attr_accessor :key, :value, :negative
+
+ def initialize(key, value)
+ @key, @value = key, value
+ end
+
+ def fulfilled?(song)
+ @value == song.value_for(@key)
+ #(@value == song.value_for(@key))^@negative
+ end
+
+ def Criteria.name value
+ Criteria.new(:name, value)
+ end
+
+ def Criteria.artist value
+ Criteria.new(:artist, value)
+ end
+
+ def Criteria.album value
+ Criteria.new(:album, value)
+ end
+
+ def |(other)
+ AtLeastOneCriteria.new(self, other)
+ end
+
+ def &(other)
+ DoubleCriteria.new(self, other)
+ end
+
+ def !
+ NegativeCriteria.new(self)
+ end
+end
+
+class DoubleCriteria
+ def initialize(first, second)
+ @first, @second = first, second
+ end
+
+ def fulfilled?(song)
+ @first.fulfilled?(song) & @second.fulfilled?(song)
+ end
+end
+
+class AtLeastOneCriteria
+ def initialize(first, second)
+ @first, @second = first, second
+ end
+
+ def fulfilled?(song)
+ @first.fulfilled?(song) | @second.fulfilled?(song)
+ end
+end
+
+class NegativeCriteria
+ def initialize(criteria)
+ @criteria = criteria
+ end
+
+ def fulfilled?(song)
+ !@criteria.fulfilled?(song)
+ end
+end