From: Pat Maddox Date: 2006-05-08T06:37:42+09:00 Subject: Re: [SOLUTION] Bracket Packing (#78) This is the very first time I've participated in the ruby quiz...I was very excited about it, and was absurdly pleased with myself when I got a working program. Then I saw the other solutions. My code is like 6x longer than everyone else's. It's kind of depressing. A lot of the code in my class is messy - I'd really like to learn how to make it work better. Despite that, it provides a decent interface. You can either just pass in the string to create an item, or do stuff like SoftWrap.new.wrap(Bracket.new) It'd be really easy to implement a slick DSL with it, but I ended up not doing so because it just didn't really matter. The capability is there though :) Anyway here's my solution. As I said, I'd love it if anyone could give me hints on making the implementation cleaner. require "enumerator" class Item @balance_left = {} @balance_right = {} @descriptors = [] attr_reader :parent def self.create(description, parent = nil) product, remainder = split_product(description) return nil if product.nil? if(product.length == 1) item = create_item(product, parent) elsif product.length == 2 return nil else item = create_item(product, parent) child = create(product[1...-1], item) item.wrap(child) unless child.nil? if item.empty? && !item.leaf? return nil end if !remainder.empty? && item.root? return nil end unless remainder.empty? || item.root? sibling = create(remainder, item) item.parent.wrap(sibling) end end return item end def self.descriptor(d = nil) unless d.nil? @descriptor = d Item.balance(descriptor[0].chr, descriptor[1].chr) if @descriptor.size == 2 end @descriptor end def self.regex descriptor.length == 2 ? "^\\#{descriptor[0].chr}(.*)\\#{descriptor[1].chr}$" : descriptor end def initialize(parent = nil) @items = [] @parent = parent end def leaf? false end def empty? @items.empty? end def wrap(i) i.parent = self @items << i self end def parent=(p) @parent = p end def root? @parent.nil? end def descriptor self.class.descriptor end def description unless @items.empty? desc = [descriptor[0].chr] @items.reverse.each { |i| desc << i.description } desc << descriptor[1].chr desc.flatten.join else descriptor end end descriptor '' protected def self.split_product(description) index = 0 elements = [] description.split(/\s*/).each_with_index do |c, index| if @descriptors.include?(c) if @balance_left.include?(c) elements.push(c) elsif @balance_right.include?(c) if elements.last == @balance_right[c] elements.pop elsif elements.size == 0 elements.push(c) end end break if elements.size == 0 end end if elements.size == 0 return description[0..index], description[index+1..-1] else return nil end end def self.balance(first_char, second_char) @balance_left[first_char] = second_char @balance_right[second_char] = first_char @descriptors << first_char unless @descriptors.include?(first_char) @descriptors << second_char unless @descriptors.include?(second_char) end def self.create_item(string, parent = nil) ObjectSpace.enum_for(:each_object, class << Item; self; end).to_a.each do |klass| next if klass == self r = Regexp.new(klass.regex) return klass.new(parent) if r.match(string) end nil end end class Bracket < Item descriptor "B" def wrap(i) raise "Brackets ain't got no flow" end def leaf? true end end class SoftWrap < Item descriptor "()" end class WoodBox < Item descriptor "{}" end class CardboardBox < Item descriptor "[]" end pkg_desc = ARGV[0] package = Item.create(pkg_desc) exit(1) if package.nil? puts package.description