Решение на Втора задача от Веселин Добрев

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

Към профила на Веселин Добрев

Резултати

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

Код

require 'forwardable'
class NumberSet
extend Forwardable
include Enumerable
def initialize
@numbers = []
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
def [](filter)
reduced_set = NumberSet.new
@numbers.select { |number| filter.passes? number }.each do |number|
reduced_set << number
end
reduced_set
end
def_delegator :@numbers, :each
end
class Filter
def initialize(&block)
@block = block
end
def passes?(number)
@block.call(number)
end
def &(other_filter)
Filter.new { |number| passes? number and other_filter.passes? number}
end
def |(other_filter)
Filter.new { |number| passes? number or other_filter.passes? number}
end
end
class TypeFilter < Filter
def initialize(type)
@type = type
end
def passes?(number)
case @type
when :integer then number.is_a? Fixnum
when :real then number.is_a? Float or number.is_a? Rational
when :complex then number.is_a? Complex
end
end
end
class SignFilter < Filter
def initialize(sign)
@sign = sign
end
def passes?(number)
case @sign
when :positive then number > 0
when :negative then number < 0
when :non_negative then number >= 0
when :non_positive then number <= 0
end
end
end

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

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

Finished in 0.02238 seconds
24 examples, 0 failures

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

Веселин обнови решението на 23.10.2014 01:15 (преди около 10 години)

+class NumberSet
+ include Enumerable
+
+ def initialize
+ @numbers = Array.new
+ end
+
+ def <<(number)
+ @numbers << number if ! @numbers.include? number
+ end
+
+ def size
+ @numbers.size
+ end
+
+ def empty?
+ @numbers.empty?
+ end
+
+ def [](filter)
+ @numbers.select { |number| filter.passes? number }
+ end
+
+ def each(&block)
+ @numbers.each do |number|
+ yield number
+ end
+ end
+end
+
+
+class Filter
+ def initialize(&block)
+ @block = block
+ end
+
+ def passes?(number)
+ @block.call(number)
+ end
+
+ def &(other_filter)
+ Filter.new { |number| passes? number and other_filter.passes? number}
+ end
+
+ def |(other_filter)
+ Filter.new { |number| passes? number or other_filter.passes? number}
+ end
+end
+
+class TypeFilter
+ def initialize(type)
+ @type = type
+ end
+
+ def passes?(number)
+ case @type
+ when :integer then number.is_a? Fixnum
+ when :real then number.is_a? Float or number.is_a? Rational
+ when :complex then number.is_a? Complex
+ end
+ end
+
+ def &(other_filter)
+ Filter.new { |number| passes? number and other_filter.passes? number}
+ end
+
+ def |(other_filter)
+ Filter.new { |number| passes? number or other_filter.passes? number}
+ end
+end
+
+class SignFilter
+ def initialize(sign)
+ @sign = sign
+ end
+
+ def passes?(number)
+ case @sign
+ when :positive then number > 0
+ when :negative then number < 0
+ when :non_negative then number >= 0
+ when :non_positive then number <= 0
+ end
+ end
+
+ def &(other_filter)
+ Filter.new { |number| passes? number and other_filter.passes? number}
+ end
+
+ def |(other_filter)
+ Filter.new { |number| passes? number or other_filter.passes? number}
+ end
+end

Веселин обнови решението на 23.10.2014 11:16 (преди около 10 години)

class NumberSet
include Enumerable
def initialize
@numbers = Array.new
end
def <<(number)
- @numbers << number if ! @numbers.include? number
+ @numbers << number unless @numbers.include? number
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
def [](filter)
@numbers.select { |number| filter.passes? number }
end
def each(&block)
@numbers.each do |number|
yield number
end
end
end
class Filter
def initialize(&block)
@block = block
end
def passes?(number)
@block.call(number)
end
def &(other_filter)
Filter.new { |number| passes? number and other_filter.passes? number}
end
def |(other_filter)
Filter.new { |number| passes? number or other_filter.passes? number}
end
end
class TypeFilter
def initialize(type)
@type = type
end
def passes?(number)
case @type
when :integer then number.is_a? Fixnum
when :real then number.is_a? Float or number.is_a? Rational
when :complex then number.is_a? Complex
end
end
def &(other_filter)
Filter.new { |number| passes? number and other_filter.passes? number}
end
def |(other_filter)
Filter.new { |number| passes? number or other_filter.passes? number}
end
end
class SignFilter
def initialize(sign)
@sign = sign
end
def passes?(number)
case @sign
when :positive then number > 0
when :negative then number < 0
when :non_negative then number >= 0
when :non_positive then number <= 0
end
end
def &(other_filter)
Filter.new { |number| passes? number and other_filter.passes? number}
end
def |(other_filter)
Filter.new { |number| passes? number or other_filter.passes? number}
end
-end
+end

Много добро решение. Имам няколко много малки забележки:

  • Използвай литерали винаги, когато можеш. [] винаги е за предпочитане пред Array.new. Конструктура на класа можеш да ползваш, ако му подаваш някакви параметри или блок.
  • NumberSet#[] трябва да връща NumberSet обект. В момента връщаш Array обект.
  • Разгледай как се държи Array#each, когато не му подадеш блок. Очакваме NumberSet#each да се държи по същия начин.
  • Харесва ми, че си помислил за името на Filter#passes?.
  • Filter#&, TypeFilter#& и SignFilter#& страшно много си приличат. Не мислиш ли, че ти трябва точно един от тях? Същото за |.
  • Пробвай да подравниш then-овете в case-a. Според мен ще постигнеш върховна симетрия.
  • Няма как да не отбележа, че имаш два празни реда между NumberSet и Filter класовете. Махни единия за да е както другите.

Веселин обнови решението на 23.10.2014 14:46 (преди около 10 години)

class NumberSet
include Enumerable
def initialize
- @numbers = Array.new
+ @numbers = []
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
def [](filter)
@numbers.select { |number| filter.passes? number }
end
- def each(&block)
- @numbers.each do |number|
- yield number
- end
+ def each
+ return to_enum(:each) unless block_given?
+ @numbers.each { |number| yield number }
end
end
+module FilterModule
+ def &(other_filter)
+ Filter.new { |number| passes? number and other_filter.passes? number}
+ end
+ def |(other_filter)
+ Filter.new { |number| passes? number or other_filter.passes? number}
+ end
+end
+
class Filter
+ include FilterModule
+
def initialize(&block)
@block = block
end
def passes?(number)
@block.call(number)
end
-
- def &(other_filter)
- Filter.new { |number| passes? number and other_filter.passes? number}
- end
-
- def |(other_filter)
- Filter.new { |number| passes? number or other_filter.passes? number}
- end
end
class TypeFilter
+ include FilterModule
+
def initialize(type)
@type = type
end
def passes?(number)
case @type
when :integer then number.is_a? Fixnum
- when :real then number.is_a? Float or number.is_a? Rational
+ when :real then number.is_a? Float or number.is_a? Rational
when :complex then number.is_a? Complex
end
end
-
- def &(other_filter)
- Filter.new { |number| passes? number and other_filter.passes? number}
- end
-
- def |(other_filter)
- Filter.new { |number| passes? number or other_filter.passes? number}
- end
end
class SignFilter
+ include FilterModule
+
def initialize(sign)
@sign = sign
end
def passes?(number)
case @sign
- when :positive then number > 0
- when :negative then number < 0
+ when :positive then number > 0
+ when :negative then number < 0
when :non_negative then number >= 0
when :non_positive then number <= 0
end
- end
-
- def &(other_filter)
- Filter.new { |number| passes? number and other_filter.passes? number}
- end
-
- def |(other_filter)
- Filter.new { |number| passes? number or other_filter.passes? number}
end
end

Успях да го посъкратя малко с модул, но не съм сигурен, че мога да обединя трите филтър класове в един. Предполагам, че ще трябва да използвам alias, обаче проблемът идва оттам, че няма как да побера всички проверки в един метод (заради ограниченията на sceptic).

П.П. Извинявам се за изпуснатия space пред @numbers в each метода :D

  • Поправи се за изпуснатия space.
  • Този each метод може да се реализира по по-прост начин. Той на практика вече е реализиран в Array. Можеш да делегираш на Array#each предавайки му блока.
  • Очакваме NumberSet#[] да върне NumberSet обект. В момента връщаш Array обект.
  • Не ти трябва модул. Защо не оставиш TypeFilter и SignFilter да наследят Filter и по този начин да reuse-нат логиката?

Освен горните коментари мисля, че решението ти е много добро. :)

Веселин обнови решението на 23.10.2014 16:03 (преди около 10 години)

+require 'forwardable'
+
class NumberSet
+ extend Forwardable
include Enumerable
def initialize
@numbers = []
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
def [](filter)
- @numbers.select { |number| filter.passes? number }
+ reduced_set = NumberSet.new
+ @numbers.select { |number| filter.passes? number }.each do |number|
+ reduced_set << number
+ end
+ reduced_set
end
- def each
- return to_enum(:each) unless block_given?
- @numbers.each { |number| yield number }
- end
+ def_delegator :@numbers, :each
end
-module FilterModule
- def &(other_filter)
- Filter.new { |number| passes? number and other_filter.passes? number}
- end
-
- def |(other_filter)
- Filter.new { |number| passes? number or other_filter.passes? number}
- end
-end
-
class Filter
- include FilterModule
-
def initialize(&block)
@block = block
end
def passes?(number)
@block.call(number)
end
-end
-class TypeFilter
- include FilterModule
+ def &(other_filter)
+ Filter.new { |number| passes? number and other_filter.passes? number}
+ end
+ def |(other_filter)
+ Filter.new { |number| passes? number or other_filter.passes? number}
+ end
+end
+
+class TypeFilter < Filter
def initialize(type)
@type = type
end
def passes?(number)
case @type
when :integer then number.is_a? Fixnum
when :real then number.is_a? Float or number.is_a? Rational
when :complex then number.is_a? Complex
end
end
end
-class SignFilter
- include FilterModule
-
+class SignFilter < Filter
def initialize(sign)
@sign = sign
end
def passes?(number)
case @sign
when :positive then number > 0
when :negative then number < 0
when :non_negative then number >= 0
when :non_positive then number <= 0
end
end
end

Нашата версия на ръководството по стил препоръчва идентиране с едно ниво навътре на when клаузите на case. И на мен ми се струва по-четимо с отместване навътре, така че препоръчвам да го промениш по този начин.

Веселин обнови решението на 26.10.2014 19:56 (преди около 10 години)

require 'forwardable'
class NumberSet
extend Forwardable
include Enumerable
def initialize
@numbers = []
end
def <<(number)
@numbers << number unless @numbers.include? number
end
def size
@numbers.size
end
def empty?
@numbers.empty?
end
def [](filter)
reduced_set = NumberSet.new
@numbers.select { |number| filter.passes? number }.each do |number|
reduced_set << number
end
reduced_set
end
def_delegator :@numbers, :each
end
class Filter
def initialize(&block)
@block = block
end
def passes?(number)
@block.call(number)
end
def &(other_filter)
Filter.new { |number| passes? number and other_filter.passes? number}
end
def |(other_filter)
Filter.new { |number| passes? number or other_filter.passes? number}
end
end
class TypeFilter < Filter
def initialize(type)
@type = type
end
def passes?(number)
case @type
- when :integer then number.is_a? Fixnum
- when :real then number.is_a? Float or number.is_a? Rational
- when :complex then number.is_a? Complex
+ when :integer then number.is_a? Fixnum
+ when :real then number.is_a? Float or number.is_a? Rational
+ when :complex then number.is_a? Complex
end
end
end
class SignFilter < Filter
def initialize(sign)
@sign = sign
end
def passes?(number)
case @sign
- when :positive then number > 0
- when :negative then number < 0
- when :non_negative then number >= 0
- when :non_positive then number <= 0
+ when :positive then number > 0
+ when :negative then number < 0
+ when :non_negative then number >= 0
+ when :non_positive then number <= 0
end
end
end