Решение на Втора задача от Николай Димитров

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

Към профила на Николай Димитров

Резултати

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

Код

class NumberSet
include Enumerable
def initialize(numbers = [])
@numbers = numbers
end
def each(&block)
@numbers.each &block
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def [](filter)
NumberSet.new @numbers.select { |number| filter.match? number }
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
end
class Filter
def initialize(&condition)
@condition = condition
end
def match?(number)
@condition.call number
end
def &(other)
Filter.new { |number| match? number and other.match? number }
end
def |(other)
Filter.new { |number| match? number or other.match? number }
end
end
class TypeFilter < Filter
def initialize(type)
case type
when :integer then super() { |number| number.is_a? Integer }
when :complex then super() { |number| number.is_a? Complex }
else super() { |number| number.is_a? Float or number.is_a? Rational }
end
end
end
class SignFilter < Filter
def initialize(sign)
case sign
when :positive then super() { |number| number > 0 }
when :non_positive then super() { |number| number <= 0 }
when :negative then super() { |number| number < 0 }
when :non_negative then super() { |number| number >= 0 }
end
end
end

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

........................

Finished in 0.02189 seconds
24 examples, 0 failures

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

Николай обнови решението на 22.10.2014 08:22 (преди над 9 години)

+class NumberSet
+ include Enumerable
+
+ def initialize(numbers = Array.new)
+ @numbers = numbers
+ end
+
+ def each
+ @numbers.each { |number| yield number }
+ end
+
+ def <<(number)
+ @numbers << number unless @numbers.include? number
+ end
+
+ def [](filter)
+ self.class.new @numbers.select { |number| filter.condition.call number }
+ end
+
+ def size
+ @numbers.size
+ end
+
+ def empty?
+ @numbers.empty?
+ end
+end
+
+class Filter
+ attr_reader :condition
+
+ def initialize(condition)
+ @condition = condition
+ end
+
+ def &(another)
+ compound_condition = lambda do |number|
+ @condition.call number and another.condition.call number
+ end
+ Filter.new compound_condition
+ end
+end
+
+class TypeFilter < Filter
+ def initialize(type)
+ @condition =
+ case type
+ when :integer then lambda { |number| number.is_a? Integer }
+ when :complex then lambda { |number| number.is_a? Complex }
+ else lambda { |number| number.is_a? Float or number.is_a? Rational }
+ end
+ end
+end
+
+class SignFilter < Filter
+ def initialize(type)
+ @condition = case type
+ when :positive then lambda { |number| number > 0 }
+ when :non_positive then lambda { |number| number <= 0 }
+ when :negative then lambda { |number| number < 0 }
+ when :non_negative then lambda { |number| number >= 0 }
+ end
+ end
+end

Еха... Това решение е толкова близо до истината още от първата версия и преди да сме предали значителна част от нещата, че няма как да не кажа голямо браво! Все пак има няколко сериозни пропуска:

  • Пусни си примерните тестове. Разгледай слайдовете на лекцията от понеделник.
  • Прочети пак условието на задачата. Липсва ти малка част от функционалността.
  • NumberSet#each в този си вид не е напълно правилна имплементация на each. Hint: виж какво трябва да става, ако не му се подаде блок. Дори и да го оправиш, няма нужда да преодкриваш колелото. Можеш просто да вземеш блока в NumberSet#each и да го подаваш на Array#each.
  • Не използвай Array.new или Hash.new like... никога. Освен ако няма да ги викаш с параметри/блок. [] и {} са в пъти по-красиви. (:
  • В Ruby се предпочита да изпускаш self където е възможно.
  • Трябваше ли ти изобщо lambda-та във Filter#&? Същото и за конструкторите на TypeFilter/SignFilter - разгледай как работи super (или изчакай до довечера да ви разкажем за него).
  • Виж как би следвало да именуваш, когато предефинираш бинарен оператор.

Николай обнови решението на 23.10.2014 01:53 (преди над 9 години)

class NumberSet
include Enumerable
- def initialize(numbers = Array.new)
+ def initialize(numbers = [])
@numbers = numbers
end
- def each
- @numbers.each { |number| yield number }
+ def each(&block)
+ @numbers.each &block
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def [](filter)
- self.class.new @numbers.select { |number| filter.condition.call number }
+ NumberSet.new @numbers.select( & filter.condition)
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
end
class Filter
attr_reader :condition
- def initialize(condition)
+ def initialize(&condition)
@condition = condition
end
- def &(another)
- compound_condition = lambda do |number|
- @condition.call number and another.condition.call number
+ def &(other)
+ Filter.new do |number|
+ @condition.call number and other.condition.call number
end
- Filter.new compound_condition
end
+
+ def |(other)
+ Filter.new do |number|
+ @condition.call number or other.condition.call number
+ end
+ end
end
class TypeFilter < Filter
def initialize(type)
@condition =
case type
- when :integer then lambda { |number| number.is_a? Integer }
- when :complex then lambda { |number| number.is_a? Complex }
- else lambda { |number| number.is_a? Float or number.is_a? Rational }
+ when :integer then -> (number) { number.is_a? Integer }
+ when :complex then -> (number) { number.is_a? Complex }
+ else -> (number) { number.is_a? Float or number.is_a? Rational }
end
end
end
class SignFilter < Filter
def initialize(type)
- @condition = case type
- when :positive then lambda { |number| number > 0 }
- when :non_positive then lambda { |number| number <= 0 }
- when :negative then lambda { |number| number < 0 }
- when :non_negative then lambda { |number| number >= 0 }
+ @condition =
+ case type
+ when :positive then -> (number){ number > 0 }
+ when :non_positive then -> (number){ number <= 0 }
+ when :negative then -> (number){ number < 0 }
+ when :non_negative then -> (number){ number >= 0 }
end
end
end

Почти перфектно. Само малки дребни работи:

  • Направил си & filter.condition магарията, за да се отървеш от skeptic проблема. По-добра алтернатива би било да добавиш един предикат във Filter, който да скрива извикването и да махнеш attr_reader :condition. Така интерфейсът ти ще е по-интуитивен за другите класове и ще скрива имплементацията.
  • Като си copy-paste-вал си забравил да смениш името на аргумента в конструктора на SignFilter.

Николай обнови решението на 24.10.2014 23:48 (преди над 9 години)

class NumberSet
include Enumerable
def initialize(numbers = [])
@numbers = numbers
end
def each(&block)
@numbers.each &block
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def [](filter)
- NumberSet.new @numbers.select( & filter.condition)
+ NumberSet.new @numbers.select { |number| filter.match? number }
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
end
class Filter
+ private
+
attr_reader :condition
+ public
+
def initialize(&condition)
@condition = condition
end
+ def match?(number)
+ @condition.call number
+ end
+
def &(other)
- Filter.new do |number|
- @condition.call number and other.condition.call number
- end
+ Filter.new { |number| match? number and other.match? number }
end
def |(other)
- Filter.new do |number|
- @condition.call number or other.condition.call number
- end
+ Filter.new { |number| match? number or other.match? number }
end
end
class TypeFilter < Filter
def initialize(type)
- @condition =
case type
- when :integer then -> (number) { number.is_a? Integer }
- when :complex then -> (number) { number.is_a? Complex }
- else -> (number) { number.is_a? Float or number.is_a? Rational }
+ when :integer then super &(-> (number) { number.is_a? Integer })
+ when :complex then super &(-> (number) { number.is_a? Complex })
+ else super &(-> (number) { number.is_a? Float or number.is_a? Rational })
end
end
end
class SignFilter < Filter
- def initialize(type)
- @condition =
- case type
- when :positive then -> (number){ number > 0 }
- when :non_positive then -> (number){ number <= 0 }
- when :negative then -> (number){ number < 0 }
- when :non_negative then -> (number){ number >= 0 }
+ def initialize(sign)
+ case sign
+ when :positive then super &(-> (number) { number > 0 })
+ when :non_positive then super &(-> (number) { number <= 0 })
+ when :negative then super &(-> (number) { number < 0 })
+ when :non_negative then super &(-> (number) { number >= 0 })
end
end
end

Николай обнови решението на 25.10.2014 08:30 (преди над 9 години)

class NumberSet
include Enumerable
def initialize(numbers = [])
@numbers = numbers
end
def each(&block)
@numbers.each &block
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def [](filter)
NumberSet.new @numbers.select { |number| filter.match? number }
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
end
class Filter
- private
-
- attr_reader :condition
-
- public
-
def initialize(&condition)
@condition = condition
end
def match?(number)
@condition.call number
end
def &(other)
Filter.new { |number| match? number and other.match? number }
end
def |(other)
Filter.new { |number| match? number or other.match? number }
end
end
class TypeFilter < Filter
def initialize(type)
case type
when :integer then super &(-> (number) { number.is_a? Integer })
when :complex then super &(-> (number) { number.is_a? Complex })
else super &(-> (number) { number.is_a? Float or number.is_a? Rational })
end
end
end
class SignFilter < Filter
def initialize(sign)
case sign
when :positive then super &(-> (number) { number > 0 })
when :non_positive then super &(-> (number) { number <= 0 })
when :negative then super &(-> (number) { number < 0 })
when :non_negative then super &(-> (number) { number >= 0 })
end
end
end

Този начин на подаване на блок е доста неприятен, тъй като си има хубав синтаксис за него:

when :integer then super &(-> (number) { number.is_a? Integer })

По-добре е да е така:

when :integer then super { |number| number.is_a? Integer }

Смятам, че си съгласен, че така е много по-добре :) Аз подозирам защо си го направил по този наичн, обаче. Вероятно проблемът ти е с аргументите. Един начин би бил просто да приемеш аргумент и във Filter#initialize, който да игнорираш. Например, def initialize(_, &condition). Друг вариант е да позволиш произволен брой аргументи, например така: def initialize(*_, &condition). Но правилният начин всъщност е друг.

super е специална ключова дума в езика. Извиква родителската версия на метода. Поведението на кръглите скоби след извикване на super е малко по-различно от всички останали методи. Когато няма кръгли скоби и няма подадени аргументи, какъвто би бил случаят с подаване на блок като синтаксис, тогава super просто предава аргументите на текущия метод към родителската версия, без да ги пипа. Ако искаш да промениш това поведение, слагаш параметри и/или кръгли скоби.

Тоест, правилното решение на проблема би било:

when :integer then super() { |number| number.is_a? Integer }

Николай обнови решението на 26.10.2014 22:17 (преди над 9 години)

class NumberSet
include Enumerable
def initialize(numbers = [])
@numbers = numbers
end
def each(&block)
@numbers.each &block
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def [](filter)
NumberSet.new @numbers.select { |number| filter.match? number }
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
end
class Filter
def initialize(&condition)
@condition = condition
end
def match?(number)
@condition.call number
end
def &(other)
Filter.new { |number| match? number and other.match? number }
end
def |(other)
Filter.new { |number| match? number or other.match? number }
end
end
class TypeFilter < Filter
def initialize(type)
case type
- when :integer then super &(-> (number) { number.is_a? Integer })
- when :complex then super &(-> (number) { number.is_a? Complex })
- else super &(-> (number) { number.is_a? Float or number.is_a? Rational })
+ when :integer then super() { |number| number.is_a? Integer }
+ when :complex then super() { |number| number.is_a? Complex }
+ else super() { |number| number.is_a? Float or number.is_a? Rational }
end
end
end
class SignFilter < Filter
def initialize(sign)
case sign
- when :positive then super &(-> (number) { number > 0 })
- when :non_positive then super &(-> (number) { number <= 0 })
- when :negative then super &(-> (number) { number < 0 })
- when :non_negative then super &(-> (number) { number >= 0 })
+ when :positive then super() { |number| number > 0 }
+ when :non_positive then super() { |number| number <= 0 }
+ when :negative then super() { |number| number < 0 }
+ when :non_negative then super() { |number| number >= 0 }
end
end
end