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