Решение на Втора задача от Деян Гюрджеклиев

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

Към профила на Деян Гюрджеклиев

Резултати

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

Код

class NumberSet
include Enumerable
def initialize
@set = []
end
def each(&block)
@set.each(&block)
end
def <<(number)
@set << number unless @set.include? number
end
def size
@set.size
end
def empty?
@set.empty?
end
def [](filter)
filtered_array = select { |number| filter.passes? (number) }
filtered_array.each_with_object(NumberSet.new) do |number, result|
result << number
end
end
end
class Filter
def initialize(&filter_rule)
@filter_rule = filter_rule
end
def passes?(number)
@filter_rule.call(number)
end
def &(other)
Filter.new { |number| passes?(number) & other.passes?(number) }
end
def |(other)
Filter.new { |number| passes?(number) | other.passes?(number) }
end
end
class TypeFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
def passes?(number)
case @filter_rule
when :integer then number.is_a? Integer
when :real then number.is_a? Float
when :complex then number.is_a? Complex
end
end
end
class SignFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
def passes?(number)
case @filter_rule
when :positive then number > 0
when :non_positive then number <= 0
when :negative then number < 0
when :non_negative then number >= 0
end
end
end

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

..........F.............

Failures:

  1) NumberSet can filter by real type
     Failure/Error: expect(filtered_numbers.size).to eq expecting.size
       
       expected: 2
            got: 1
       
       (compared using ==)
     # /tmp/d20141028-18133-1ywbhwo/spec.rb:180:in `can_filter'
     # /tmp/d20141028-18133-1ywbhwo/spec.rb:55:in `block (2 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.02251 seconds
24 examples, 1 failure

Failed examples:

rspec /tmp/d20141028-18133-1ywbhwo/spec.rb:54 # NumberSet can filter by real type

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

Деян обнови решението на 22.10.2014 16:20 (преди над 9 години)

+class NumberSet
+ include Enumerable
+
+ attr_accessor :set
+
+ def initialize
+ @set = []
+ end
+
+ def each
+ @set.each { |element| yield element }
+ end
+
+ def <<(number)
+ @set << number unless @set.include? number
+ end
+
+ def size
+ @set.size
+ end
+
+ def empty?
+ @set.empty?
+ end
+
+ def [](filter)
+ @set.select & filter.get_filter
+ end
+
+end
+
+class AbstractFilter
+
+ def &(other)
+ predicate = lambda{ |x| get_filter.call(x) & other.get_filter.call(x) }
+ Filter.new(&predicate)
+ end
+
+ def |(other)
+ predicate = lambda { |x| get_filter.call(x) | other.get_filter.call(x) }
+ Filter.new(&predicate)
+ end
+
+end
+
+
+class Filter < AbstractFilter
+
+ def initialize(&filter_rule)
+ @filter_rule = filter_rule
+ end
+
+ def get_filter
+ @filter_rule
+ end
+
+end
+
+class TypeFilter < AbstractFilter
+
+ def initialize(filter_rule)
+ @filter_rule = filter_rule
+ end
+
+ def get_filter
+ case @filter_rule
+ when :integer then lambda { |element| element.is_a? Integer }
+ when :real then lambda { |element| element.is_a? Float }
+ when :complex then lambda { |element| element.is_a? Complex }
+ end
+ end
+
+end
+
+class SignFilter < AbstractFilter
+
+ def initialize(filter_rule)
+ @filter_rule = filter_rule
+ end
+
+ def get_filter
+ case @filter_rule
+ when :positive then lambda { |element| element > 0 }
+ when :non_positive then lambda { |element| element <= 0 }
+ when :negative then lambda { |element| element < 0 }
+ when :non_negative then lambda { |element| element >= 0 }
+ end
+ end
+
+end

Имам проблем. Това е моето решение, за жалост не работи, тъй като се опитвам да го кача и ми излиза следното съобщение "Намерихме няколко грешки. Погледнете и пробвайте пак: Spaces around operators * no spaces around & on line 27". Промених го само, за да го кача ( знам, че не работи ).@set.select & filter.get_filter = @set.select(&filter.get_filter) в оригиналното решение. Благодаря предварително :)

Здрасти,

Решението ти ми харесва. Имам няколко коментара, които можеш да погледнеш:

  • Не са ти необходими getter и setter за set. Махни го този attr_accessor.
  • Не оставяй празен ред преди първия и след последния метод в клас.
  • Провери как работи Array#each, когато не му подадем блок. Очакваме NumberSet#each да работи по същия начин.
  • Помисли дали са ти необходими lambda-ите в AbstractFilter#& и AbstractFilter#|. Учихме и други интересни работи за анонимните функции в Ruby.
  • Защо ти е AbstractFilter? Защо не оставиш TypeFilter и SignFilter да наследяват Filter?
  • Не ми харесва get_filter метода. Защо трябва да показва анонимната функция на всички? Какво мислиш за това да го замениш с метод, който приема число и проверява дали то удовлетворява филтъра? Така ще скриеш анонимната функция зад ясно дефиниран интерфейс. Според мен кодът ти ще стане по-четим. Особено, ако уцелиш правилното име за такъв метод. :)

Грешката, която получаваше е проблем от наша страна. Работим по решаването. Ако погледнеш последната точка горе и направиш нещо подобно не би трябвало да имаш проблем с предаването на решението.

Успех! :)

Деян обнови решението на 23.10.2014 13:59 (преди над 9 години)

class NumberSet
include Enumerable
-
- attr_accessor :set
-
def initialize
@set = []
end
- def each
- @set.each { |element| yield element }
+ def each(&block)
+ @set.each(&block)
end
def <<(number)
@set << number unless @set.include? number
end
def size
@set.size
end
def empty?
@set.empty?
end
def [](filter)
- @set.select & filter.get_filter
+ @set.select { |number| filter.predicate(number) }
end
-
end
-class AbstractFilter
-
- def &(other)
- predicate = lambda{ |x| get_filter.call(x) & other.get_filter.call(x) }
- Filter.new(&predicate)
+class Filter
+ def initialize(&filter_rule)
+ @filter_rule = filter_rule
end
- def |(other)
- predicate = lambda { |x| get_filter.call(x) | other.get_filter.call(x) }
- Filter.new(&predicate)
+ def predicate(number)
+ @filter_rule.call(number)
end
-end
-
-
-class Filter < AbstractFilter
-
- def initialize(&filter_rule)
- @filter_rule = filter_rule
+ def &(other_filter)
+ Filter.new { |number| predicate(number) & other_filter.predicate(number) }
end
- def get_filter
- @filter_rule
+ def |(other_filter)
+ Filter.new { |number| predicate(number) | other_filter.predicate(number) }
end
-
end
-class TypeFilter < AbstractFilter
-
+class TypeFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def get_filter
+ def predicate(number)
case @filter_rule
- when :integer then lambda { |element| element.is_a? Integer }
- when :real then lambda { |element| element.is_a? Float }
- when :complex then lambda { |element| element.is_a? Complex }
+ when :integer then number.is_a? Integer
+ when :real then number.is_a? Float
+ when :complex then number.is_a? Complex
end
end
-
end
-class SignFilter < AbstractFilter
-
+class SignFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def get_filter
+ def predicate(number)
case @filter_rule
- when :positive then lambda { |element| element > 0 }
- when :non_positive then lambda { |element| element <= 0 }
- when :negative then lambda { |element| element < 0 }
- when :non_negative then lambda { |element| element >= 0 }
+ when :positive then number > 0
+ when :non_positive then number <= 0
+ when :negative then number < 0
+ when :non_negative then number >= 0
end
end
-
end

Още няколко неща:

  • Празен ред след include Enumerable
  • Очакваме NumberSet#[] да връща NumberSet обект. В момента връщаш Array обект.
  • Сигурен ли си, че Filter#predicate е най-подходящото име за този метод? Имаме конвенция за методи, които връщат булева стойност.
  • В style guide-а е описано как трябва да наименоваш аргументите на бинарни оператори като & и |. Погледни го.

Деян обнови решението на 23.10.2014 17:11 (преди над 9 години)

class NumberSet
include Enumerable
+
def initialize
@set = []
end
def each(&block)
@set.each(&block)
end
def <<(number)
@set << number unless @set.include? number
end
def size
@set.size
end
def empty?
@set.empty?
end
def [](filter)
- @set.select { |number| filter.predicate(number) }
+ result = NumberSet.new
+ select { |number| filter.apply?(number) }.each { |number| result << number }
+ result
end
end
class Filter
def initialize(&filter_rule)
@filter_rule = filter_rule
end
- def predicate(number)
+ def apply?(number)
@filter_rule.call(number)
end
- def &(other_filter)
- Filter.new { |number| predicate(number) & other_filter.predicate(number) }
+ def &(other)
+ Filter.new { |number| apply?(number) & other.apply?(number) }
end
- def |(other_filter)
- Filter.new { |number| predicate(number) | other_filter.predicate(number) }
+ def |(other)
+ Filter.new { |number| apply?(number) | other.apply?(number) }
end
end
class TypeFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def predicate(number)
+ def apply?(number)
case @filter_rule
when :integer then number.is_a? Integer
when :real then number.is_a? Float
when :complex then number.is_a? Complex
end
end
end
class SignFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def predicate(number)
+ def apply?(number)
case @filter_rule
when :positive then number > 0
when :non_positive then number <= 0
when :negative then number < 0
when :non_negative then number >= 0
end
end
-end
+end

Имам няколко въпроса. Този ли е начинът по който да връщаме нов NumberSet обект от []? Можеше да направя конструктора да приема array, но така щях да разваля инварианта на класа, ако се подаде array с повтарящи се елементи. Второ, знам че конвенцията е да се пише "?" в края на predicate метод, но в този случай не е ли най-логично, името на този метод да бъде apply(без ?)? Не се сещам за нищо по-добро, затова просто прибавих "?" след apply.

Ако приемаш Array обект в конструктора, можеш да ползваш Array#uniq в initialize метода. Твоят вариант е ok. Можеш да разгледаш Enumerable#each_with_object и да се възползваш от него.

Какво ще кажеш за имена от сорта на passes?, matches?, satisfied_by?, accepts??

Деян обнови решението на 27.10.2014 10:40 (преди над 9 години)

class NumberSet
include Enumerable
def initialize
@set = []
end
def each(&block)
@set.each(&block)
end
def <<(number)
@set << number unless @set.include? number
end
def size
@set.size
end
def empty?
@set.empty?
end
def [](filter)
- result = NumberSet.new
- select { |number| filter.apply?(number) }.each { |number| result << number }
- result
+ filtered_array = select { |number| filter.passes? (number) }
+ filtered_array.each_with_object(NumberSet.new) do |number, result|
+ result << number
+ end
end
end
class Filter
def initialize(&filter_rule)
@filter_rule = filter_rule
end
- def apply?(number)
+ def passes?(number)
@filter_rule.call(number)
end
def &(other)
- Filter.new { |number| apply?(number) & other.apply?(number) }
+ Filter.new { |number| passes?(number) & other.passes?(number) }
end
def |(other)
- Filter.new { |number| apply?(number) | other.apply?(number) }
+ Filter.new { |number| passes?(number) | other.passes?(number) }
end
end
class TypeFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def apply?(number)
+ def passes?(number)
case @filter_rule
when :integer then number.is_a? Integer
when :real then number.is_a? Float
when :complex then number.is_a? Complex
end
end
end
class SignFilter < Filter
def initialize(filter_rule)
@filter_rule = filter_rule
end
- def apply?(number)
+ def passes?(number)
case @filter_rule
when :positive then number > 0
when :non_positive then number <= 0
when :negative then number < 0
when :non_negative then number >= 0
end
end
end