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

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

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

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 10 успешни тест(а)
  • 1 неуспешни тест(а)

Код

class Song
attr_reader :name, :artist, :album
def initialize(song)
@name, @artist, @album = song
end
def appropriate?(criteria)
return conjuction?(criteria) if criteria.kind == :&
return union?(criteria) if criteria.kind == :|
return negation?(criteria) if criteria.kind.length >= 7
singles?(criteria)
end
def singles?(criteria)
return (name == criteria.keywords) if criteria.kind == :name
return (artist == criteria.keywords) if criteria.kind == :artist
album == criteria.keywords if criteria.kind == :album
end
def conjuction?(criteria)
list = [name, artist, album] - criteria.keywords
list.length <= 1
end
def union?(criteria)
list = criteria.keywords - [name, artist, album]
list.length <= 1
end
def negation?(clause)
return (not (name == clause.keywords)) if clause.kind == :not_name
return (not (artist == clause.keywords)) if clause.kind == :not_artist
not album == clause.keywords if clause.kind == :not_album
end
end
class Collection
include Enumerable
attr_accessor :songs
def initialize(songs = [])
@songs = songs
end
def each(&block)
@songs.each(&block)
end
def self.parse(text)
songs_collection = []
list = text.split("\n").select { | x | not x == "" }
list.each_slice(3) { | song | songs_collection << Song.new(song) }
new songs_collection
end
def names
map { | song | song.name }.uniq
end
def artists
map { | song | song.artist }.uniq
end
def albums
map { | song | song.album }.uniq
end
def filter(criteria)
sub_collection = select { | song | song.appropriate?(criteria) }
Collection.new sub_collection
end
def adjoin(collection)
Collection.new songs + collection.songs
end
end
class Criteria
include Enumerable
attr_accessor :keywords, :kind
def initialize(keywords = [], kind = :no_kind)
@keywords = keywords
@kind = kind
end
def each(&block)
@keywords.each(&block)
end
def self.name(song_name)
new song_name, :name
end
def self.artist(artist_name)
new artist_name, :artist
end
def self.album(album_name)
new album_name, :album
end
def &(other_criteria)
Criteria.new [keywords, other_criteria.keywords], :&
end
def |(other_criteria)
Criteria.new [keywords, other_criteria.keywords], :|
end
def !
current_kind = :not_name if kind == :name
current_kind = :not_artist if kind == :artist
current_kind = :not_album if kind == :album
Criteria.new keywords, current_kind
end
end

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

.........F.

Failures:

  1) Collection supports negation of filters
     Failure/Error: filtered.map(&:name).should eq ['Mad About You']
       
       expected: ["Mad About You"]
            got: ["Fields of Gold"]
       
       (compared using ==)
     # /tmp/d20130203-23049-12z47nv/spec.rb:79:in `block (2 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/homework/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.01042 seconds
11 examples, 1 failure

Failed examples:

rspec /tmp/d20130203-23049-12z47nv/spec.rb:77 # Collection supports negation of filters

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

Иван обнови решението на 30.10.2012 13:00 (преди над 11 години)

+class Song
+ def initialize(song)
+ @name = song[0]
+ @artist = song[1]
+ @album = song[2]
+ end
+
+ def appropriate?(criteria)
+ return self.conjuction?(criteria) if criteria.kind == 4
+ return self.union?(criteria) if criteria.kind == 5
+ return self.negation?(criteria) if criteria.kind >= 100
+ self.singles?(criteria)
+ end
+
+ def singles?(criteria)
+ return (name == criteria.instance[0]) if criteria.kind == 1
+ return (artist == criteria.instance[0]) if criteria.kind == 2
+ return (album == criteria.instance[0]) if criteria.kind == 3
+ end
+
+ def conjuction?(criteria)
+ new_array = [name, artist, album] - criteria.instance
+ return new_array.length <= 1
+ end
+
+ def union?(criteria)
+ new_array = criteria.instance - [name, artist, album]
+ return new_array.length <= 1
+ end
+
+ def negation?(criteria)
+ return (not (name == criteria.instance[0])) if criteria.kind == 100
+ return (not (artist == criteria.instance[0])) if criteria.kind == 200
+ return (not (album == criteria.instance[0])) if criteria.kind == 300
+ end
+
+ attr_reader :name, :artist, :album
+end
+
+class Collection
+ include Enumerable
+
+ def initialize
+ @songs = []
+ end
+
+ def each(&block)
+ @songs.each(&block)
+ end
+
+ def Collection.parse(text)
+ songs_collection = Collection.new
+ list = text.split("\n").select { | x | not x == "" }
+ list.each_slice(3) { | trio | songs_collection.songs << Song.new(trio) }
+ songs_collection
+ end
+
+ def names
+ names = []
+ each { | song | names << song.name }
+ names.uniq
+ end
+
+ def artists
+ artists = []
+ each { | song | artists << song.artist }
+ artists.uniq
+ end
+
+ def albums
+ albums = []
+ each { | song | albums << song.album }
+ albums.uniq
+ end
+
+ def filter(criteria)
+ sub_collection = Collection.new
+ sub_collection.songs = select { | song | song.appropriate? (criteria) }
+ sub_collection
+ end
+
+ def adjoin(collection)
+ sub_collection = Collection.new
+ sub_collection.songs = songs + collection.songs
+ sub_collection
+ end
+
+ attr_accessor :songs
+end
+
+class Criteria
+ include Enumerable
+
+ def initialize
+ @instance = []
+ @kind = 0
+ end
+
+ def each(&block)
+ @instance.each(&block)
+ end
+
+ def Criteria.name(song_name)
+ criteria = Criteria.new
+ criteria.instance[0] = song_name
+ criteria.kind = 1
+ criteria
+ end
+
+ def Criteria.artist(artist_name)
+ criteria = Criteria.new
+ criteria.instance[0] = artist_name
+ criteria.kind = 2
+ criteria
+ end
+
+ def Criteria.album(album_name)
+ criteria = Criteria.new
+ criteria.instance[0] = album_name
+ criteria.kind = 3
+ criteria
+ end
+
+ def &(other_criteria)
+ criteria = Criteria.new
+ criteria.instance = instance + other_criteria.instance
+ criteria.kind = 4
+ criteria
+ end
+
+ def |(other_criteria)
+ criteria = Criteria.new
+ criteria.instance = instance + other_criteria.instance
+ criteria.kind = 5
+ criteria
+ end
+
+ def !
+ criteria = Criteria.new
+ criteria.instance = instance
+ criteria.kind = 100 * kind
+ criteria
+ end
+
+ attr_accessor :instance, :kind
+end

Нали знаеш, че на конструктора на клас можеш да подаваш аргументи? :)

Кодът в Criteria трябва да ти намирисва, понеже има страшно много повторение в инстанционните методи. Това повторение може да го минимизираш по различни начини, но поне добави някакви аргументи на конструктора и го направи така. Ето пример как може да изглежда метода ти Criteria.name:

def self.name(song_name)
  new song_name, 1
end

Забележи няколко неща:

  • Ползвам def self.name за да дефинирам класов метод
  • Не викам Criteria.new, защото в този контекст self и Criteria са едно и също, а както знаем, new е нормално извикване на метод и ако не посочим получател, този получател се подразбира като self. Т.е. следните три неща са едно и също в този контекст: Criteria.new, self.new и new.
  • Направо връщам създадената инстанция, след като съм подал аргументи на конструктора

Допълнителни бележки:

  • Това с числата за kind в Criteria е доста нетипично за Ruby, даже бих казал, че е нетипично и за езици като Java и C, където човек би си дефинирал поне един ENUM или някакви константи. Тук в Ruby символите са много подходящи за тази цел.
  • Мисля, че за това, за което го ползваш, полето instance на Criteria е неподходящо именовано.
  • Дефинирай клас методи със def self.method_name, вместо def ClassName.method_name.
  • По конвенция attr_accessor неща обикновено се слагат в началото на класа, а не в дъното му.
  • Въпреки, че още не сме говорили в детайли за това, ред 3 до 5 могат да станат така: @name, @artist, @album = song.
  • Пропускай self. пред извикването на методи, това почти винаги е ненужно (напр. в Song#appropriate?).
  • new_array не е добро име на променлива (за instance вече споменах). Други лоши имена: trio
  • Пропускай return, когато се намира пред последно оценения израз в метод (напр. в методите conjunction?, union? и т.н.)
  • На ред 78 не трябва да има интервал тук: song.appropriate? (criteria), или трябва да махнеш скобите.
  • Не модифицирай полето Collection#songs отвън; така колекцията ти става mutable; направи я immutable и просто й подавай списъка с песни в конструктора. Целият ти код лесно може да се обърне в тази посока. Споменавали сме на лекции, че immutable обекти се предпочитат и сме казвали защо.
  • За методите names, artists, albums и прочее си припомни какво прави Enumerable#map. Ако кодът ти остане в този вариант в тези методи, ще ти вземем точки.

Иван обнови решението на 30.10.2012 21:29 (преди над 11 години)

class Song
+ attr_reader :name, :artist, :album
+
def initialize(song)
- @name = song[0]
- @artist = song[1]
- @album = song[2]
+ @name, @artist, @album = song
end
def appropriate?(criteria)
- return self.conjuction?(criteria) if criteria.kind == 4
- return self.union?(criteria) if criteria.kind == 5
- return self.negation?(criteria) if criteria.kind >= 100
- self.singles?(criteria)
+ return conjuction?(criteria) if criteria.kind == :&
+ return union?(criteria) if criteria.kind == :|
+ return negation?(criteria) if criteria.kind.length >= 7
+ singles?(criteria)
end
def singles?(criteria)
- return (name == criteria.instance[0]) if criteria.kind == 1
- return (artist == criteria.instance[0]) if criteria.kind == 2
- return (album == criteria.instance[0]) if criteria.kind == 3
+ return (name == criteria.keywords) if criteria.kind == :name
+ return (artist == criteria.keywords) if criteria.kind == :artist
+ album == criteria.keywords if criteria.kind == :album
end
def conjuction?(criteria)
- new_array = [name, artist, album] - criteria.instance
- return new_array.length <= 1
+ list = [name, artist, album] - criteria.keywords
+ list.length <= 1
end
def union?(criteria)
- new_array = criteria.instance - [name, artist, album]
- return new_array.length <= 1
+ list = criteria.keywords - [name, artist, album]
+ list.length <= 1
end
- def negation?(criteria)
- return (not (name == criteria.instance[0])) if criteria.kind == 100
- return (not (artist == criteria.instance[0])) if criteria.kind == 200
- return (not (album == criteria.instance[0])) if criteria.kind == 300
+ def negation?(clause)
+ return (not (name == clause.keywords)) if clause.kind == :not_name
+ return (not (artist == clause.keywords)) if clause.kind == :not_artist
+ not album == clause.keywords if clause.kind == :not_album
end
-
- attr_reader :name, :artist, :album
end
class Collection
include Enumerable
- def initialize
- @songs = []
+ attr_accessor :songs
+
+ def initialize(songs = [])
+ @songs = songs
end
def each(&block)
@songs.each(&block)
end
- def Collection.parse(text)
- songs_collection = Collection.new
+ def self.parse(text)
+ songs_collection = []
list = text.split("\n").select { | x | not x == "" }
- list.each_slice(3) { | trio | songs_collection.songs << Song.new(trio) }
- songs_collection
+ list.each_slice(3) { | song | songs_collection << Song.new(song) }
+ new songs_collection
end
def names
- names = []
- each { | song | names << song.name }
- names.uniq
+ map { | song | song.name }.uniq
end
def artists
- artists = []
- each { | song | artists << song.artist }
- artists.uniq
+ map { | song | song.artist }.uniq
end
def albums
- albums = []
- each { | song | albums << song.album }
- albums.uniq
+ map { | song | song.album }.uniq
end
def filter(criteria)
- sub_collection = Collection.new
- sub_collection.songs = select { | song | song.appropriate? (criteria) }
- sub_collection
+ sub_collection = select { | song | song.appropriate?(criteria) }
+ Collection.new sub_collection
end
def adjoin(collection)
- sub_collection = Collection.new
- sub_collection.songs = songs + collection.songs
- sub_collection
+ Collection.new songs + collection.songs
end
-
- attr_accessor :songs
end
class Criteria
include Enumerable
- def initialize
- @instance = []
- @kind = 0
+ attr_accessor :keywords, :kind
+
+ def initialize(keywords = [], kind = :no_kind)
+ @keywords = keywords
+ @kind = kind
end
def each(&block)
- @instance.each(&block)
+ @keywords.each(&block)
end
- def Criteria.name(song_name)
- criteria = Criteria.new
- criteria.instance[0] = song_name
- criteria.kind = 1
- criteria
+ def self.name(song_name)
+ new song_name, :name
end
- def Criteria.artist(artist_name)
- criteria = Criteria.new
- criteria.instance[0] = artist_name
- criteria.kind = 2
- criteria
+ def self.artist(artist_name)
+ new artist_name, :artist
end
- def Criteria.album(album_name)
- criteria = Criteria.new
- criteria.instance[0] = album_name
- criteria.kind = 3
- criteria
+ def self.album(album_name)
+ new album_name, :album
end
def &(other_criteria)
- criteria = Criteria.new
- criteria.instance = instance + other_criteria.instance
- criteria.kind = 4
- criteria
+ Criteria.new [keywords, other_criteria.keywords], :&
end
def |(other_criteria)
- criteria = Criteria.new
- criteria.instance = instance + other_criteria.instance
- criteria.kind = 5
- criteria
+ Criteria.new [keywords, other_criteria.keywords], :|
end
def !
- criteria = Criteria.new
- criteria.instance = instance
- criteria.kind = 100 * kind
- criteria
+ current_kind = :not_name if kind == :name
+ current_kind = :not_artist if kind == :artist
+ current_kind = :not_album if kind == :album
+ Criteria.new keywords, current_kind
end
-
- attr_accessor :instance, :kind
end