Tuesday, August 28, 2007

String Concatenation

I made a small mistake in the code in "Promises and String Concatenation". The DelayString object not being stateless is a huge problem. Here's the test to prove it:
    def test_independence
first = "3" + "4"
second = first + "5"
third = "2" + second
assert_equal("34", first.to_s)
assert_equal("345", second.to_s)
assert_equal("2345", third.to_s)
end

Ouch. I fixed it by making DelayString only know a left and a right part. It cleaned the code up quite a bit. I factored out Promise because it made the code a little less readable. The resulting code is much simpler:
class DelayString

def initialize(oneString, anotherString)
@left = oneString
@right = anotherString
end

def +(another)
DelayString.new(self, another)
end

def to_s()
return @result unless @result.nil?
to_process=[self]
@result=String.stream_contents do |out|
until (to_process.empty?)
current=to_process.pop
current.process(to_process,out)
end
end
end

def process(to_process,io)
to_process.push(@right)
to_process.push(@left)
end

end



class String

def self.stream_contents(&monadic)
StringIO.open() do |io|
monadic.call(io)
io.string
end
end

def +(another)
DelayString.new(self, another)
end

def process(to_process,io)
io << self
end

end

All of our tests run. DelayString is stateless (minus caching of the result). There's still improvements to be made, but the code is simpler and easier to understand. The performance did take a hit. It's twice as slow (43.94s) as the previous version. Not to worry it still beats normal concatenation by a large margin. I'll take the performance hit for more readable code anyday!

No comments: