Раздел «Язык Ruby».RubyCourseLecture08:
<<Метапрограммирование на Ruby

Следующая лекция
Предыдущая лекция

Лекция 8. Реализация класса Set

Класс Set

Класс Set присутствует в стандартной библиотеке Ruby. Попробуем реализовать его сами.

def extract_options(ary)
    if ary.last.is_a?(Hash)
        ary.pop
    else
        {}
    end
end

class Module
    def delegate(*args)
        options = extract_options(args)
        to = options[:to]
        args.each do |method|
            define_method(method) do |*args|
                send(to).send(method, *args)
            end
        end
    end
end

class Set
    def initialize(*elements)
        @data = {}
        elements.each do |e| 
           @data[e] = true
        end
    end
    
    attr_accessor :data
    private :data
    
    delegate :size, :delete, :include?, :to => :data
    
    def insert(x)
        @data[x] = true
    end
    
    alias :<< :insert
    
    def each(&block)
        @data.each_key do |key|
            block[key]
        end
    end

    alias :=== :include? 

    def to_a
        @data.keys
    end
    
    def join!(s)
        if s.respond_to?(:each)
            s.each do |e|
                self << e
            end
            self
        else 
            raise ArgumentError
        end
    end
    
    def +(s)
        self.dup.join!(s)
    end
    
    def minus!(s)
        if s.respond_to?(:each)
            s.each do |e|
                delete(e)
            end
            self
        else 
            raise ArgumentError
        end
    end
    
    def -(s)
        self.dup.minus!(s)
    end
    
    def intersect!(s)
        if s.respond_to?(:each)
            s = s.to_set
            to_a.each do |e|
                self.delete(e) unless s.include?(e)
            end
        else 
            raise ArgumentError
        end
    end

    include Enumerable
    
end

Метод to_set

Добавим метод to_set всем контейнерам сразу:

Enumerable.module_eval do
    def to_set
        inject(Set.new) do |s,e|
            s << e
        end
    end
end

Тестирование

# здесь опущено определение класса Set,
# данное выше

if $0 == __FILE__
    require 'test/unit'
    class SetTest < Test::Unit::TestCase
        
        def ttttinitialize(test_method)
            @s = Set.new
            @s << 1
            @s << 2
            @s << 1
            test_method.to_sym
        end
        
        def test_include
            @s = Set.new
            @s << 1
            @s << 2
            @s << 1
            assert(@s.include?(1))
            assert(@s.include?(2))
            assert(!@s.include?(3))
            assert_equal([1,2], @s.to_a.sort)
        end
        
        def test_delete
            @s = Set.new
            @s << 1
            @s << 2
            @s << 1
            @s.delete(1)
            assert(!@s.include?(1))
            assert(@s.include?(2))
        end
        
        def test_plus_and_minus
            a = [1,2,3,4,5,6]
            b = [4,3,1,6,7,8,9]
            
            sa = Set.new(*a)
            sb = Set.new(*b)
            sc = Set.new(1,2,3)
            
            assert((a - b).uniq.sort, (sa - sb).to_a.sort)
            assert((a + b).uniq.sort, (sa + sb).to_a.sort)
        end
    end
end

Следующая лекция
Предыдущая лекция

-- ArtemVoroztsov - 22 Apr 2010