Ruby:如何只允许某种对象添加到集合中

时间:2013-12-13 02:53:21

标签: ruby types set enumerable

我想创建一个Set,它只允许添加某种(kind_of?)个对象,并在尝试添加外来对象时引发异常。

我还没有找到任何资源,在我开始搞乱ruby核心课程之前,我会感激任何建议如何以“不显眼”的方式实现这一目标。

另外:我的目标实际上是创建一个“模板化”容器类,就像在C ++中一样(例如Set),这样我就可以在实例定义时设置一次类型,并在类级别上比较两个集合,如果它们是相同(即接受相同的类型)但保持与“默认”集的互操作性,作为示例set_instance_a.class == set_instance_b.class如果它们接受相同类型的对象,则应该为true。

我的想法是重载::[]运算符,以便我可以编写类似my_set = MySet[Foo]的内容,它应返回仅接受MySet类型对象的Foo实例

谢谢!

2 个答案:

答案 0 :(得分:3)

require "set"

class MySet < Set
  def add e
    raise "Invalid element #{e}" unless e.kind_of?(Foo)
    super(e)
  end
end

或者,如果您不想使用子类,则:

require "set"

class Set
  alias original_add :add
  def add e
    raise "Invalid element #{e}" unless e.kind_of?(Foo)
    original_add(e)
  end
end

编辑于2017年添加 要做到这一点,不要以现代方式进行子类化,

require "set"

module MySet
  def add e
    raise "Invalid element #{e}" unless e.kind_of?(Foo)
    super(e)
  end
end

class Set
  prepend MySet
end

答案 1 :(得分:3)

Class也是一个对象,因此您可以根据需要在运行时创建新类。 Class构造函数允许您指定基类,甚至可以在新类的上下文中评估代码:

  

new(super_class = Object)→a_class
   new(super_class = Object){| mod | ......}→a_class

     

使用给定的超类创建一个新的匿名(未命名)类(如果没有给出参数,则为Object)。您可以通过将类对象赋值给常量来为类赋予名称。

     

如果给出了一个块,它将传递给该类对象,并使用class_eval在该类的上下文中计算该块。

class_eval允许您定义方法和别名。这一切意味着你可以这样说:

module Kernel
  def MySet(c)
    Class.new(Set) do
      @@allowed = c
      def add(o)
        raise ArgumentError.new("#{o.class} is stinky!") if(!o.is_a?(@@allowed))
        super(o)
      end
      alias_method :<<, :add
    end
  end
end

然后:

s = MySet(String).new
s.add(6)        # Exception!
s << 'pancakes' # Allowed

我将MySet方法修补为Kernel,以便与ArrayComplex,......方法 - 假装成为函数一致,你可以把它放在任何你想要的地方。