关于如何在Ruby中实现某些问题的指导

时间:2014-10-21 16:59:50

标签: ruby class filter

对于类NumberSet,我必须定义[]方法,以便它接受一个参数,它用作过滤器来选择集合的成员并返回包含这些元素的新集合

例如,我有

Filter.new { |number| number.even? }

SignFilter.new(:non_negative)

这是我必须构建的类。

我还必须定义&|运算符,以便它们可以使用过滤器。

的内容
numbers[SignFilter.new(:non_negative) & Filter.new { |number| number.even? }]

到目前为止的课程是:

class NumberSet
  include Enumerable

  def initialize
    @arr=[]
  end
  def each (&block)
    @arr.each do |member|
      block.call (member)
    end
  end
  def << number
    @arr<<number unless  @arr.include?(number)
  end
end

对于Filter我觉得有点像:

class Filter
  def initialize
    yield
  end
end

我最大的问题是[]$|部分,我不知道该怎么做。


我想要完成的是:


numbers = NumberSet.new
  [-3, -2, -1, 0, 1, 2, 3, 4, 5].each do |number|
  numbers << number
end
numbers[SignFilter.new(:non_negative) & Filter.new { |number| number.even?}].to_a
#=>[0, 2, 4]

2 个答案:

答案 0 :(得分:0)

由于这似乎是一个家庭作业问题,我不打算全面实施,但我会在你的路上看到你。您需要认识到的最重要的事情是,[]|等方法可以定义为Ruby中的常规方法。在Ruby中,您只需将消息(例如[])发送到&#34;接收器&#34;。如何处理这些方法是通过类定义上的def指令定义的。所以,例如:

 class Filter
   def |(other_filter)
     # Do some work to build a new filter which is a union of this filter and other_filter
   end
 end

 class SignFilter < Filter
    # SignFilter inherits the & method from Filter, and you can then go on to implement SignFilter-specific functionality
    # ...
 end

 class NumberSet
   def [](filter)
     # Given a filter, apply each of the numbers of this NumberSet to the filter, and return those which pass the filter
   end
 end

要调用这些方法,可以执行以下操作:

 filter = Filter.new {|num| num.even? }
 sign_filter = SignFilter.new(:non_negative)
 union_filter = filter | sign_filter

这第三行相当于调用:

 union_filter = filter.|(sign_filter)

或:

 union_filter = filter.send("|", sign_filter)

在所有三种情况下,&#34; |&#34;消息被发送到filter实例,其中sign_filter作为附加参数。你也可以这样做:

 numset = NumberSet.new(1,2,3,4,5)
 filtered_numbers = numset[union_filter]

这第二行相当于:

 numset.[](union_filter)
 # or
 numset.send("[]", union_filter)

原始语法只是那些方法调用的语法糖 - 它只是因为它使Ruby代码看起来更好,并帮助程序员在心理上将它映射到这些操作的常用约定。

答案 1 :(得分:0)

我认为应该这样做。

<强>代码

SignFilter类

class SignFilter
  def initialize(sign)
    @sign = sign
  end
  def filter
    case @sign
    when :negative
      -> i { i < 0 }
    when :positive
      -> i { i > 0 }
    when :non_negative
      -> i { i >= 0 }
    when :non_positive
      -> i { i <= 0 }
    when :zero
      -> i { i.zero? }
    end
  end
end

过滤类

class Filter
  attr_reader :filter
  def initialize &filter
    @filter = filter
  end
end

NumberSet类

class NumberSet
  def initialize arr
    @arr = arr
  end
  def process(op, sf, f)
    sign_filter = sf.filter
    filter = f.filter 
    @arr.send(op).each { |e| sign_filter.call(e) }
        .send(op).each { |e| filter.call(e) } 
  end
end

<强>实施例

sf = SignFilter.new :negative
f = Filter.new { |number| number.even? }
ns = NumberSet.new [-3, -2, -1, 0, 1, 2, 3, 4, 5]
ns.process(:reject, sf, f)
  #=> [1, 3, 5]

sf = SignFilter.new :non_negative
f = Filter.new { |number| number % 3 == 0 }
ns = NumberSet.new [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
ns.process(:select, sf, f)
  #=> [0, 3, 6]

<强>解释

块不是对象,因此您无法传递它们。但是,它们可以转换为procs,它们是对象(类Proc的实例),因此可以传递。你需要了解procs(和lambdas,它们是proc的一种类型)。 Here是一个很好的解释。 (旁白:有一点让人感到困惑的是,可以通过多种方式调用它们。例如,如果p -> i { 3*i }p.call(2)p.yield(2)p[2]和{ {1}}都返回值p.(2)。)

以下是发生的事情:

  • 通过传递表示要选择或拒绝的数组元素符号的符号来创建6类的实例sfSignFilter,{{1 }} 等等)。符号保存在实例变量:negative中。
  • :non_negative类的方法@sign根据SignFilter的值返回filter
  • lambda类的实例是通过传递一个块来创建的,该块作为@sign接收并保存到具有读取访问器的实例变量Filter中。
  • 通过传递数组创建proc类的实例,该数组保存在实例变量@filter中。
  • NumberSet传递三个参数:可枚举方法@arrNumberSet#process(保存到:reject),:select实例(op )和SignFilter实例(sf)。
  • Filter内,f已发送NumberSet#process,这会创建一个枚举器(@arrop
  • 在枚举器的每个元素上调用arr.reject lambda。返回由arr.select)的子集合组成的数组。
  • 返回的数组发送SignFilter,创建另一个@arrop枚举器。
  • 在新枚举器的每个元素上调用reject proc,导致返回所需的数组。

考虑到selectFilter都不是必需的(要么会这样做),只需将reject中的select替换为.send(op)就更简单了{ {1}},但我选择这样做只是为了说明概括。

请注意,将NumberSet#process概括为reject很容易,现在,NumberSet#processprocess(sf,f,o)提供过滤器,sf将是f类的实例,它指定要发送到已过滤的子集合o的proc,以使用块(Operation@arr等调用Enumerable方法)。