Александър обнови решението на 31.10.2012 02:09 (преди около 12 години)
+class Collection < Array
+ def self.parse(raw_data)
+ collection = Collection.new
+ raw_data.lines.map(&:chomp).each_slice(4) do |lines|
+ collection.push Song.new lines[0], lines[1], lines[2]
+ end
+ collection
+ end
+
+ def artists
+ map(&:artist).uniq
+ end
+
+ def names
+ map(&:name).uniq
+ end
+
+ def albums
+ map(&:album).uniq
+ end
+
+ def filter(expression)
+ Collection.new(select { |song| expression.match song })
+ end
+
+ def adjoin(other)
+ result = Collection.new self
+ result.concat other
+ end
+end
+
+class Song
+ attr_accessor :name, :artist, :album
+
+ def initialize(name, artist, album)
+ @name, @artist, @album = name, artist, album
+ end
+end
+
+class Criteria
+ attr_accessor :type, :value
+
+ attr_writer :positive
+
+ def initialize(type, value, positive = true)
+ @type = type
+ @value = value
+ @positive = positive
+ end
+
+ def positive?
+ @positive
+ end
+
+ def self.name(value)
+ Expression.new :and, Criteria.new(:name, value)
+ end
+
+ def self.artist(value)
+ Expression.new :and, Criteria.new(:artist, value)
+ end
+
+ def self.album(value)
+ Expression.new :and, Criteria.new(:album, value)
+ end
+
+ def !()
+ Criteria.new @type, @value, !positive?
+ end
+
+ def match(song)
+ song.send(@type) == @value
+ end
+end
+
+class Expression
+ attr_accessor :type, :variables
+
+ def initialize(type, variables = [])
+ @type = type
+ @variables = variables
+ end
+
+ def &(other)
+ if other.type == @type and @type == :and
+ Expression.new :and, [@variables, other.variables].flatten
+ else
+ Expression.new :and, [self, other]
+ end
+ end
+
+ def |(other)
+ if other.type == @type and @type == :or
+ Expression.new :or, [@variables, other.variables].flatten
+ else
+ Expression.new :or, [self, other]
+ end
+ end
+
+ def !()
+ new_type = @type == :and ? :or : :and
+ Expression.new new_type, @variables.map { |variable| !variable }
+ end
+
+ def match(thing)
+ return @variables.match thing if @variables.respond_to?(:match)
+
+ if @type == :and
+ _match_all_variables thing
+ else
+ _match_at_least_one_variable thing
+ end
+ end
+
+ def _match_all_variables thing
+ @variables.all? { |variable| variable.match thing }
+ end
+
+ def _match_at_least_one_variable thing
+ @variables.any? { |variable| variable.match thing }
+ end
+end