Решение на Втора задача от Красимир Георгиев

Обратно към всички решения

Към профила на Красимир Георгиев

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 11 успешни тест(а)
  • 0 неуспешни тест(а)

Код

# ,-. _.---._
# | `\.__.-'' `.
# \ _ _ ,. \
# ,+++=.____mtv.rb_______)_||______|_|_|| |
# (_.ooo.===================||======|=|=|| |
# ~~' by comco | ~' `~' o o /
# \ /~`\ o o /
# `~' `-.____.-'
class Collection
include Enumerable
Properties = [:name, :artist, :album]
attr_reader :songs, :names, :artists, :albums
# the collection is initialized by a table of hashes, representing the
# attributes of a specific song
def initialize(songs)
@songs = songs
# get the attributes of the collection by looking at the columns of the
# table
table = songs.map(&:values)
@names, @artists, @albums = *table.transpose.map(&:uniq)
end
def self.parse(text)
# split the input into an array of song-specific lines
blocks = text.each_line.map(&:strip).each_slice(4)
# transform each block of lines to a hash with attributes
songs = blocks.map { |block| Hash[Properties.zip(block)] }
new songs
end
Song = Struct.new(*Properties)
def each
@songs.each do |song|
# for iteration, construct a new song with the required methods
yield Song.new(*song.values)
end
end
def filter(criteria)
filtered_songs = @songs.select { |song| criteria.check.(song) }
Collection.new filtered_songs
end
def adjoin(other)
merged_songs = @songs | other.songs
Collection.new merged_songs
end
end
# a criteria contains a member which is a predicate for checking the criteria
class Criteria < Struct.new(:check)
# base criterias definition
def self.name(song_name)
new ->(song) { song[:name] == song_name }
end
def self.artist(artist_name)
new ->(song) { song[:artist] == artist_name }
end
def self.album(album_name)
new ->(song) { song[:album] == album_name }
end
# criteria composition support
def !
Criteria.new ->(song) { not check.(song) }
end
def |(other)
Criteria.new ->(song) { check.(song) or other.check.(song) }
end
def &(other)
Criteria.new ->(song) { check.(song) and other.check.(song) }
end
end

Лог от изпълнението

...........

Finished in 0.0114 seconds
11 examples, 0 failures

История (3 версии и 3 коментара)

Красимир обнови решението на 26.10.2012 19:06 (преди около 12 години)

+# ,-. _.---._
+# | `\.__.-'' `.
+# \ _ _ ,. \
+# ,+++=.____mtv.rb_______)_||______|_|_|| |
+# (_.ooo.===================||======|=|=|| |
+# ~~' by comco | ~' `~' o o /
+# \ /~`\ o o /
+# `~' `-.____.-'
+class Collection
+ include Enumerable
+
+ Properties = [:name, :artist, :album]
+ attr_reader :songs, :names, :artists, :albums
+
+ # the collection is initialized by a table of hashes, representing the
+ # attributes of a specific song
+ def initialize(songs)
+ @songs = songs
+ # get the attributes of the collection by looking at the columns of the
+ # table
+ @names, @artists, @albums = *@songs.map(&:values).transpose.map(&:uniq)
+ end
+
+ def self.parse(text)
+ # split the input into an array of song-specific lines
+ blocks = text.each_line.map(&:chomp).each_slice(4)
+ # transform each block of lines to a hash with attributes
+ songs = blocks.map { |block| Hash[Properties.zip(block)] }
+ new songs
+ end
+
+ Song = Struct.new(*Properties)
+
+ def each
+ @songs.each do |song|
+ # for iteration, construct a new song with the required methods
+ yield Song.new(*song.values)
+ end
+ end
+
+ def filter(criteria)
+ filtered_songs = @songs.select(&criteria.check)
+ Collection.new filtered_songs
+ end
+
+ def adjoin(other)
+ merged_songs = @songs | other.songs
+ Collection.new merged_songs
+ end
+end
+
+# a criteria contains a member which is a predicate for checking the criteria
+class Criteria < Struct.new(:check)
+ # base criterias definition
+ def self.name(song_name)
+ new ->(song) { song[:name] == song_name }
+ end
+
+ def self.artist(artist_name)
+ new ->(song) { song[:artist] == artist_name }
+ end
+
+ def self.album(album_name)
+ new ->(song) { song[:album] == album_name }
+ end
+
+ # criteria composition support
+ def !
+ Criteria.new ->(song) { not check.(song) }
+ end
+
+ def |(other)
+ Criteria.new ->(song) { check.(song) or other.check.(song) }
+ end
+
+ def &(other)
+ Criteria.new ->(song) { check.(song) and other.check.(song) }
+ end
+end

Красимир обнови решението на 30.10.2012 00:11 (преди около 12 години)

# ,-. _.---._
# | `\.__.-'' `.
# \ _ _ ,. \
# ,+++=.____mtv.rb_______)_||______|_|_|| |
# (_.ooo.===================||======|=|=|| |
# ~~' by comco | ~' `~' o o /
# \ /~`\ o o /
# `~' `-.____.-'
class Collection
include Enumerable
Properties = [:name, :artist, :album]
attr_reader :songs, :names, :artists, :albums
# the collection is initialized by a table of hashes, representing the
# attributes of a specific song
def initialize(songs)
@songs = songs
# get the attributes of the collection by looking at the columns of the
# table
@names, @artists, @albums = *@songs.map(&:values).transpose.map(&:uniq)
end
def self.parse(text)
# split the input into an array of song-specific lines
blocks = text.each_line.map(&:chomp).each_slice(4)
# transform each block of lines to a hash with attributes
songs = blocks.map { |block| Hash[Properties.zip(block)] }
new songs
end
Song = Struct.new(*Properties)
def each
@songs.each do |song|
# for iteration, construct a new song with the required methods
yield Song.new(*song.values)
end
end
def filter(criteria)
- filtered_songs = @songs.select(&criteria.check)
+ filtered_songs = @songs.select { |song| criteria.check.(song) }
Collection.new filtered_songs
end
def adjoin(other)
merged_songs = @songs | other.songs
Collection.new merged_songs
end
end
# a criteria contains a member which is a predicate for checking the criteria
class Criteria < Struct.new(:check)
# base criterias definition
def self.name(song_name)
new ->(song) { song[:name] == song_name }
end
def self.artist(artist_name)
new ->(song) { song[:artist] == artist_name }
end
def self.album(album_name)
new ->(song) { song[:album] == album_name }
end
# criteria composition support
def !
Criteria.new ->(song) { not check.(song) }
end
def |(other)
Criteria.new ->(song) { check.(song) or other.check.(song) }
end
def &(other)
Criteria.new ->(song) { check.(song) and other.check.(song) }
end
end

Красимир обнови решението на 30.10.2012 00:24 (преди около 12 години)

# ,-. _.---._
# | `\.__.-'' `.
# \ _ _ ,. \
# ,+++=.____mtv.rb_______)_||______|_|_|| |
# (_.ooo.===================||======|=|=|| |
# ~~' by comco | ~' `~' o o /
# \ /~`\ o o /
# `~' `-.____.-'
class Collection
include Enumerable
Properties = [:name, :artist, :album]
attr_reader :songs, :names, :artists, :albums
# the collection is initialized by a table of hashes, representing the
# attributes of a specific song
def initialize(songs)
@songs = songs
# get the attributes of the collection by looking at the columns of the
# table
- @names, @artists, @albums = *@songs.map(&:values).transpose.map(&:uniq)
+ table = songs.map(&:values)
+ @names, @artists, @albums = *table.transpose.map(&:uniq)
end
def self.parse(text)
# split the input into an array of song-specific lines
- blocks = text.each_line.map(&:chomp).each_slice(4)
+ blocks = text.each_line.map(&:strip).each_slice(4)
# transform each block of lines to a hash with attributes
songs = blocks.map { |block| Hash[Properties.zip(block)] }
new songs
end
Song = Struct.new(*Properties)
def each
@songs.each do |song|
# for iteration, construct a new song with the required methods
yield Song.new(*song.values)
end
end
def filter(criteria)
filtered_songs = @songs.select { |song| criteria.check.(song) }
Collection.new filtered_songs
end
def adjoin(other)
merged_songs = @songs | other.songs
Collection.new merged_songs
end
end
# a criteria contains a member which is a predicate for checking the criteria
class Criteria < Struct.new(:check)
# base criterias definition
def self.name(song_name)
new ->(song) { song[:name] == song_name }
end
def self.artist(artist_name)
new ->(song) { song[:artist] == artist_name }
end
def self.album(album_name)
new ->(song) { song[:album] == album_name }
end
# criteria composition support
def !
Criteria.new ->(song) { not check.(song) }
end
def |(other)
Criteria.new ->(song) { check.(song) or other.check.(song) }
end
def &(other)
Criteria.new ->(song) { check.(song) and other.check.(song) }
end
end

Да, определено. Това е една от по-често срещаните грешки - хората просто забравят, че и initialize може да приема блок.

Ето някои неща които бих и не бих направил на твое място:

  • Бих инициализарал песните в Collection.parse. Инициализането в each може да е добра идея за бавни неща като заявки към бази данни, за което има смисъл да се оценява по-късно. В случаите като този, не е от голямо значение, дори забавя ако ще итерираш колекцията повече от веднъж.
  • Бих минал и без Collection::Properties.
  • Не бих онаследил от Struct.new в Criteria. Struct се ползва главно когато ти трябва някакъв запис (record, тъй де, Паскал :)) и може би операции върху неговите свойства. Не е добра идея да го ползваш само и само да си спестиш присвояването на instance променлива в initialize.
  • Като цяло и операциятя за проверяване бих я изнесъл в метод, така няма да ми се налага да показвам имплементационен детайл навън. Интерфейсът също би бил по-добър.

Благодаря за коментара.

  • Това, че инициализирам песните в each е просто следствие от първия design decision да пазя песните в колекцията под формата на хеш. Това има предимство по две причини - първата е, че по този начин различните полета се третират по еднакъв начин и втората е, че мога да гледам на хеша като цяло. Ако пазих списък от обекти, нямаше да мога да направя инициализацията на допълнителните полета на ред 21-22. Също така, този тип представяне би помогнал по-лесно да се избегне повторението на редове 56-66. Както си споменал, този подход има и недостатъци, например че трябва да създавам обектите в each, но в задачата е стриктно упоменато, че ние трябва сами да изберем подхода на имплементация.
  • Като се има предвид горното решение, Collection::Properties придобива смисъл, защото първо ми хареса номера, който показаха с Hash[] и за да използваме, че zip работи, като се съобразява с първия си аргумент, т.е. на ред 29 се получава автоматично игнориране на всеки празен четвърти ред от файла. Другият вариант би бил да изброя атрибутите в масив, но това е парче информация, което би имало смисъл да се използва на други места, като например при премахване на дубликацията 56-66.
  • Това за Struct.new го прочетох в style-guide-а: Consider using Struct.new, which defines the trivial accessors, constructor and comparison operators for you. Като цяло след като предадох задачата, се натъкнах на един много гаден проблем с използването на Struct.new и презареждането на код в irb link, и, взимайки го предвид, съм съгласен със забележката.
  • Съгласен съм, че това с директното извикване на ламбдата е кофти интерфейс.