Решение на Втора задача от Гюлджан Купен

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

Към профила на Гюлджан Купен

Резултати

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

Код

class NumberSet
include Enumerable
def initialize(array = [])
@array = array
end
def each
iterator = 0
while iterator <= @array.size
yield @array[iterator]
iterator += 1
end
end
def <<(number)
unless @array.include?(number)
@array << number
end
end
def size
@array.size
end
def empty?
@array.empty?
end
def [](filter)
NumberSet.new(filter.filter_numbers(@array))
end
end
class CombineFilters
def initialize(first_filter, second_filter, operator)
@first_filter = first_filter
@second_filter = second_filter
@operator = operator
end
def filter_numbers(array)
if @operator == :&
@first_filter.filter_numbers(@second_filter.filter_numbers(array))
else @operator == :|
new_array = @first_filter.filter_numbers(array)
new_array + @second_filter.filter_numbers(array)
end
end
end
class Filter
def initialize(&block)
@block = block
end
def filter_numbers(array)
array.select {|element| @block.(element)}
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end
class TypeFilter
def initialize(type)
@type = type
end
def filter_numbers(array)
case @type
when :integer then array.select {|number| number.is_a? Integer}
when :complex then array.select {|number| number.is_a? Complex}
else array.select {|number| number.is_a? Float or number.is_a? Rational}
end
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end
class SignFilter
def initialize(sign)
@sign = sign
end
def filter_numbers(array)
case @sign
when :positive then array.select {|number| number > 0}
when :non_positive then array.select {|number| number <= 0}
when :negative then array.select {|number| number < 0}
when :non_negative then array.select {|number| number >= 0}
end
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end

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

.................FFFF.FF

Failures:

  1) NumberSet can combine two filters with "or" rule
     Failure/Error: expect(filtered_numbers.size).to eq expecting.size
       
       expected: 7
            got: 10
       
       (compared using ==)
     # /tmp/d20141028-18133-ps8dy1/spec.rb:180:in `can_filter'
     # /tmp/d20141028-18133-ps8dy1/spec.rb:99: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)>'

  2) NumberSet can combine multiple filters with "and" rule
     Failure/Error: filter        = non_negative & non_zero & mod_3_is_zero
     NoMethodError:
       undefined method `&' for #<CombineFilters:0xb96f2bec>
     # /tmp/d20141028-18133-ps8dy1/spec.rb:108: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)>'

  3) NumberSet can combine multiple filters with "or" rule
     Failure/Error: filter        = even | negative | more_than_100
     NoMethodError:
       undefined method `|' for #<CombineFilters:0xb96f20ac>
     # /tmp/d20141028-18133-ps8dy1/spec.rb:118: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)>'

  4) NumberSet can combine multiple filters with "and" and "or" rules
     Failure/Error: filter        = even & negative | mod_3_is_zero
     NoMethodError:
       undefined method `|' for #<CombineFilters:0xb96f15bc>
     # /tmp/d20141028-18133-ps8dy1/spec.rb:128: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)>'

  5) NumberSet is enumerable
     Failure/Error: expect(values.size).to eq [Rational(5, 2), 8, 7, 9].size
       
       expected: 4
            got: 5
       
       (compared using ==)
     # /tmp/d20141028-18133-ps8dy1/spec.rb:155: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)>'

  6) NumberSet returns enumerable of set's contents if no block is given to each
     Failure/Error: expect(numbers.each.to_a.size).to eq [1, 3, 5].size
     LocalJumpError:
       no block given (yield)
     # /tmp/d20141028-18133-ps8dy1/solution.rb:11:in `each'
     # /tmp/d20141028-18133-ps8dy1/spec.rb:164: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.02155 seconds
24 examples, 6 failures

Failed examples:

rspec /tmp/d20141028-18133-ps8dy1/spec.rb:97 # NumberSet can combine two filters with "or" rule
rspec /tmp/d20141028-18133-ps8dy1/spec.rb:104 # NumberSet can combine multiple filters with "and" rule
rspec /tmp/d20141028-18133-ps8dy1/spec.rb:114 # NumberSet can combine multiple filters with "or" rule
rspec /tmp/d20141028-18133-ps8dy1/spec.rb:124 # NumberSet can combine multiple filters with "and" and "or" rules
rspec /tmp/d20141028-18133-ps8dy1/spec.rb:144 # NumberSet is enumerable
rspec /tmp/d20141028-18133-ps8dy1/spec.rb:159 # NumberSet returns enumerable of set's contents if no block is given to each

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

Гюлджан обнови решението на 24.10.2014 22:11 (преди около 10 години)

+class NumberSet
+ include Enumerable
+
+ attr_reader :array
+
+ def initialize(array = [])
+ @array = array
+ end
+
+ def each
+ iterator = 0
+ while iterator <= @array.size
+ yield @array[iterator]
+ iterator += 1
+ end
+ end
+
+ def <<(number)
+ if @array.include?(number) == false
+ @array << number
+ end
+ end
+
+ def size
+ @array.size
+ end
+
+ def empty?
+ @array.empty?
+ end
+
+ def [](filter)
+ if filter.is_a? Filter
+ filter.filter_number_set(@array)
+ elsif filter.is_a? TypeFilter
+ filter.type_filter_number_set(@array)
+ else
+ filter.sign_filter_number_set(@array)
+ end
+ end
+
+end
+
+
+class Filter
+ attr_reader :block
+
+ def initialize(&block)
+ @block = block
+ end
+
+ def filter_number_set(array)
+ NumberSet.new(array.select {|element| @block.(element)})
+ end
+end
+
+class TypeFilter
+ attr_reader :type
+
+ def initialize(type)
+ @type = type
+ end
+
+ def type_filter_number_set(array)
+ case @type
+ when :integer then NumberSet.new(array.select {|pop| pop.is_a? Integer})
+ when :complex then NumberSet.new(array.select {|pop| pop.is_a? Complex})
+ else
+ NumberSet.new(array.select {|pop| pop.is_a? Float or pop.is_a? Rational})
+ end
+ end
+end
+
+class SignFilter
+ attr_reader :sign
+
+ def initialize(sign)
+ @sign = sign
+ end
+
+ def sign_filter_number_set(array)
+ case @sign
+ when :positive
+ NumberSet.new(array.select {|pop| pop > 0})
+ when :non_positive
+ NumberSet.new(array.select {|pop| pop <= 0})
+ when :negative then NumberSet.new(array.select {|pop| pop < 0})
+ when :non_negative then NumberSet.new(array.select {|pop| pop >= 0})
+ end
+ end
+end

Здрасти,

Имам няколко коментара по решението ти.

  • Това, което си направила в NumberSet#each е може би начинът, по който е имплементиран Array#each. Мислиш ли, че можеш да го ползваш на готово? :)
  • Тези reader-и за array, block, type и sign не са ти необходими.
  • if @array.include?(number) == false е еквивалентно на unless @array.include?(number). Второто е по-кратко и четимо.

В NumberSet#[] правиш проверка за типа на филтъра. Това не е яко в Ruby. Правиш го само, защото методите ти в различните филтри се казват по различен начин. Представи си, че не беше така. Вместо filter_number_set, type_filter_number_set и sign_filter_number_set имаш метод, който се казва filter. И в трите класа: Filter#filter, TypeFilter#filter и SignFilter#filter. Тогава NumberSet#[] ще изглежда така:

def [](filter)
    filter.filter(@array)
end

Това наричаме Duck typing. Сега независимо какъв обект подадеш като аргумент на този метод (без значение от какъв тип е), стига този обект да отговаря на метода filter, всичко ще е ток. В езика няма типове, няма нужда изкуствено да ги симулираме. :)

P.S. Ако се спреш на горната реализация на filter метода, можеш да изнесеш това повтарящо се NumberSet.new в него. Така ще можеш и да си подравниш case-овете във филтрите по-добре. А, и по-добре му измисли по-добро име. :))

Гюлджан обнови решението на 25.10.2014 11:43 (преди около 10 години)

class NumberSet
include Enumerable
- attr_reader :array
-
def initialize(array = [])
@array = array
end
def each
- iterator = 0
- while iterator <= @array.size
- yield @array[iterator]
- iterator += 1
- end
+ @array.each
end
def <<(number)
- if @array.include?(number) == false
+ unless @array.include?(number)
@array << number
end
end
def size
@array.size
end
def empty?
@array.empty?
end
def [](filter)
- if filter.is_a? Filter
- filter.filter_number_set(@array)
- elsif filter.is_a? TypeFilter
- filter.type_filter_number_set(@array)
- else
- filter.sign_filter_number_set(@array)
- end
+ NumberSet.new(filter.filter_numbers(@array))
end
end
class Filter
- attr_reader :block
-
def initialize(&block)
@block = block
end
- def filter_number_set(array)
- NumberSet.new(array.select {|element| @block.(element)})
+ def filter_numbers(array)
+ array.select {|element| @block.(element)}
end
end
class TypeFilter
- attr_reader :type
-
def initialize(type)
@type = type
end
- def type_filter_number_set(array)
+ def filter_numbers(array)
case @type
- when :integer then NumberSet.new(array.select {|pop| pop.is_a? Integer})
- when :complex then NumberSet.new(array.select {|pop| pop.is_a? Complex})
- else
- NumberSet.new(array.select {|pop| pop.is_a? Float or pop.is_a? Rational})
+ when :integer then array.select {|number| number.is_a? Integer}
+ when :complex then array.select {|number| number.is_a? Complex}
+ else array.select {|number| number.is_a? Float or number.is_a? Rational}
end
end
end
class SignFilter
- attr_reader :sign
-
def initialize(sign)
@sign = sign
end
- def sign_filter_number_set(array)
+ def filter_numbers(array)
case @sign
- when :positive
- NumberSet.new(array.select {|pop| pop > 0})
- when :non_positive
- NumberSet.new(array.select {|pop| pop <= 0})
- when :negative then NumberSet.new(array.select {|pop| pop < 0})
- when :non_negative then NumberSet.new(array.select {|pop| pop >= 0})
+ when :positive then array.select {|number| number > 0}
+ when :non_positive then array.select {|number| number <= 0}
+ when :negative then array.select {|number| number < 0}
+ when :non_negative then array.select {|number| number >= 0}
end
end
end

Гюлджан обнови решението на 26.10.2014 10:57 (преди около 10 години)

class NumberSet
include Enumerable
def initialize(array = [])
@array = array
end
def each
- @array.each
+ iterator = 0
+ while iterator <= @array.size
+ yield @array[iterator]
+ iterator += 1
+ end
end
def <<(number)
unless @array.include?(number)
@array << number
end
end
def size
@array.size
end
def empty?
@array.empty?
end
def [](filter)
NumberSet.new(filter.filter_numbers(@array))
end
+end
+class CombineFilters
+ def initialize(first_filter, second_filter, operator)
+ @first_filter = first_filter
+ @second_filter = second_filter
+ @operator = operator
+ end
+
+
+ def filter_numbers(array)
+ if @operator == :&
+ @first_filter.filter_numbers(@second_filter.filter_numbers(array))
+ else @operator == :|
+ new_array = @first_filter.filter_numbers(array)
+ new_array + @second_filter.filter_numbers(array)
+ end
+ end
end
class Filter
def initialize(&block)
@block = block
end
def filter_numbers(array)
array.select {|element| @block.(element)}
end
+
+ def &(filter)
+ CombineFilters.new(self, filter, :&)
+ end
+
+ def |(filter)
+ CombineFilters.new(self, filter, :|)
+ end
end
class TypeFilter
def initialize(type)
@type = type
end
def filter_numbers(array)
case @type
when :integer then array.select {|number| number.is_a? Integer}
when :complex then array.select {|number| number.is_a? Complex}
else array.select {|number| number.is_a? Float or number.is_a? Rational}
end
end
+
+ def &(filter)
+ CombineFilters.new(self, filter, :&)
+ end
+
+ def |(filter)
+ CombineFilters.new(self, filter, :|)
+ end
end
class SignFilter
def initialize(sign)
@sign = sign
end
def filter_numbers(array)
case @sign
when :positive then array.select {|number| number > 0}
when :non_positive then array.select {|number| number <= 0}
when :negative then array.select {|number| number < 0}
when :non_negative then array.select {|number| number >= 0}
end
+ end
+
+ def &(filter)
+ CombineFilters.new(self, filter, :&)
+ end
+
+ def |(filter)
+ CombineFilters.new(self, filter, :|)
end
end

Това не работи, тъй като трябва някъде да извикаш блока, който ти се подава. Т.е да yield-ваш. Другия вариант (който е по-удачен в случая) е просто да подадеш блока на Array#each и да го оставиш той да се оправя. Припомни си как можеш да вземеш блок като параметър и как можеш да го подадеш на @array.each.

@Гюлджан, това което ти правиш, е все едно да напишеш следното:

some_list = [1, 2, 5, 100, 15]

some_list.each

Какво става? Ами, нищо. Нищо не си казала на each и each нищо не прави. Не гърми, защото връща един специален обект, но и не обхожда нищо. Същото става и с твоето решение.

Гюлджан обнови решението на 27.10.2014 16:24 (преди около 10 години)

class NumberSet
include Enumerable
def initialize(array = [])
@array = array
end
def each
iterator = 0
while iterator <= @array.size
yield @array[iterator]
iterator += 1
end
end
def <<(number)
unless @array.include?(number)
@array << number
end
end
def size
@array.size
end
def empty?
@array.empty?
end
def [](filter)
NumberSet.new(filter.filter_numbers(@array))
end
end
class CombineFilters
def initialize(first_filter, second_filter, operator)
@first_filter = first_filter
@second_filter = second_filter
@operator = operator
end
-
def filter_numbers(array)
if @operator == :&
@first_filter.filter_numbers(@second_filter.filter_numbers(array))
else @operator == :|
new_array = @first_filter.filter_numbers(array)
new_array + @second_filter.filter_numbers(array)
end
end
end
class Filter
def initialize(&block)
@block = block
end
def filter_numbers(array)
array.select {|element| @block.(element)}
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end
class TypeFilter
def initialize(type)
@type = type
end
def filter_numbers(array)
case @type
when :integer then array.select {|number| number.is_a? Integer}
when :complex then array.select {|number| number.is_a? Complex}
else array.select {|number| number.is_a? Float or number.is_a? Rational}
end
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end
class SignFilter
def initialize(sign)
@sign = sign
end
def filter_numbers(array)
case @sign
when :positive then array.select {|number| number > 0}
when :non_positive then array.select {|number| number <= 0}
when :negative then array.select {|number| number < 0}
when :non_negative then array.select {|number| number >= 0}
end
end
def &(filter)
CombineFilters.new(self, filter, :&)
end
def |(filter)
CombineFilters.new(self, filter, :|)
end
end