Решение на Втора задача от Камен Станев

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

Към профила на Камен Станев

Резултати

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

Код

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

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

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

Finished in 0.02305 seconds
24 examples, 0 failures

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

Камен обнови решението на 21.10.2014 23:55 (преди над 9 години)

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

Идеално! Много ми харесва решението.

Имам два много дребни коментара:

  • Не слагай празен ред преди първия метод дефиниран в клас.
  • Когато дефинираш оператор кръщавай параметъра other. Примерно def &(other). Дребно нещо, което помага за подобряване на консистентността.

Няколко неща, които аз бих направил, но не смятам за абсолютно правилни/фатални:

  • Бих наименовал метода Filter#satisfies?/Filter#matches? или нещо подобно вместо Filter#call. Това мисля, че ще ми направи кода една идея по-четим. Така в метода & ще имам нещо от сорта на Filter.new { |x| self.satisfies? x and other.satisfies? x }
  • Бих подравнил then клаузите в case за да се изравнят super извикванията. В случая са доста подобни и мисля, че ще изглежда прилично.

Anyways, според мен решението ти заслужава бонус точки.

Edit

Пропуснах да спомена, че NumberSet#[] трябва да връща NumberSet обект :)

Първоначалното решение което се опитах да направя наследяваше Array и Proc, но по някаква причина не успях да override-на initialize метода. До колкото разбрах не е добра практика да се наследяват core обекти защото може да се държат по-странно от нормални ruby обекти, което мисля усетих с Proc. Та от там остана и call метода. Ще пусна поправено решение малко по-късно днес. И за NumberSet#[] също.

@mitio Ако не сложиш скоби след super се препредават параметрите от горния метод. Това било един от малкото случаи в ruby в които скобите имат значение.

Камен обнови решението на 22.10.2014 10:53 (преди над 9 години)

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

@Камене, прав си за скобите. Не внимавах в картинката и не се усетих, че това искаш да постигнеш :)

Наследяването на вградени типове се прави доста рядко. Обикновено просто wrap-ваш въпросния обект и делегираш към методите му и/или правиш нещо като include Enumerable. Това е достатъчно.

Определено не наследяваш, за да получиш някаква част от методите на родителя. Наследяването трябва да се ползва само когато релацията ти наистина е "класът Дете е по-специализирана версия на класа Родител, добавяща/променяща малко функционалност".