From: "Mauricio Fernández" Date: 2003-07-19T16:52:57+09:00 Subject: Re: Ruby Golf: Object Diff --X1bOJ3K7DJ5YkBrT Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Sat, Jul 19, 2003 at 12:20:39PM +0900, John W. Long wrote: > The problem: I solved it, being more or less focused on size (but not really a lot) and when I was about to 'optimize' it I read that size doesn't matter :-) Well, you can find attached my first take. Note that I changed a couple things in the test cases when I either couldn't figure out the semantics or thought there was something wrong. > string1, string2 = ObjectDiff.compare(object1, object2) > puts [string1, string2] > assert_equal('#', string1) > assert_equal('#', string2) instance variables are output in lexicographical order > complexObject1 = TestObject1.new('1', {:a => 1, :b => 2, :j => 3}, [1, 2, you seem to assume that hash keys are sorted before printing (which isn't always possible, but...) [I assumed hashes would always be sortable with sort {|a,b|a.to_s <=> b.to_s} which is not really sensible] > puts [string1, string2] > assert_equal('#1, :b=>2, ...}, @c=[1, ... 3], > @d=#, ...>', string1) > assert_equal('#2, :b=># @c="c", @b="b", @e=nil, @a="a", @d="d">, ...}, @c=[0, ...], but iv are not here?? (You're using #inspect instead of a recursive call to object_to_s) [I corrected that to have the same format as "top level" object_to_s] > assert_equal('[... 2, 3, ... ...]', string_a) > assert_equal('[... 3, 4, ... ... 6]', string_b) In arrays, '...' is used for the first element if equal to that othe other array [kept that] > assert_equal('{:a=>1, :b=>2, ... :d=>4}', string_a) > assert_equal('{ :b=>3, ... :d=>5, :j=>2}', string_b) ... which is not the case for hashes: spaces instead??? [changed to use '...' instead, as in arrays] And again, these semantics won't always work. What happens if you have keys from different classes, how do you get a meaningful order there? Perhaps sorting by class name? My solution + the test case modifications are attached. It only passes the tests, there's a couple more things that need to be done for general use (just adding a couple 'when' clauses for other literals). -- _ _ | |__ __ _| |_ ___ _ __ ___ __ _ _ __ | '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \ | |_) | (_| | |_\__ \ | | | | | (_| | | | | |_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_| Running Debian GNU/Linux Sid (unstable) batsman dot geo at yahoo dot com "Who is General Failure and why is he reading my hard disk?" Microsoft spel chekar vor sail, worgs grate !! -- Felix von Leitner, leitner@inf.fu-berlin.de --X1bOJ3K7DJ5YkBrT Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="objectdiff.rb" require 'test/unit' class TestObject1 attr_accessor :a, :b, :c, :d, :e def initialize(a, b, c, d, e = nil) @a, @b, @c, @d, @e = a, b, c, d, e end end class TestObject2 attr_accessor :a, :b, :c, :d, :f, :g def initialize(a, b, c, d, f, g) @a, @b, @c, @d, @f, @g = a, b, c, d, f, g end end class ObjectDiff class << self def compare(o1, o2) compare_same_class(o1, o2) end def compare_same_class(o1, o2) return object_to_s(o1, o2), object_to_s(o2, o1) end def compare_array(a1, a2) return object_to_s(a1, a2), object_to_s(a2, a1) end def compare_hash(h1, h2) return object_to_s(h1, h2), object_to_s(h2, h1) end def object_to_s(obj, other = nil) case obj when NilClass return 'nil' when Fixnum return obj.inspect when String return obj.inspect when Array return obj.inspect unless Array === other r = "[" obj.each_with_index do |o,i| if i < other.size && o == other[i] r << (i == 0? '': ' ') + "..." elsif i < other.size r << (i == 0? '': ' ') + object_to_s(o, other[i]) r << ',' unless i == obj.size - 1 else r << (i == 0? '': ' ') + object_to_s(o) r << ',' unless i == obj.size - 1 end end return r << "]" when Hash keys = obj.keys.sort{|a,b| a.to_s <=> b.to_s} r = "{" keys.each_with_index do |k, i| if Hash === other and other.key?(k) and obj[k] == other[k] r << (i == 0? '': ' ') + "..." elsif Hash === other and other.key?(k) r << (i == 0? '': ' ') + k.inspect + "=>" + object_to_s(obj[k], other[k]) r << ',' unless i == keys.size - 1 else r << (i == 0? '': ' ') + k.inspect + "=>" + object_to_s(obj[k]) r << ',' unless i == keys.size - 1 end end return r << '}' end r = "#<#{obj.class}:0x%x" % obj.__id__ vars = obj.instance_variables vars.sort.each_with_index do |iv, i| if !other || !other.instance_variables.include?(iv) || other.instance_eval(iv) != obj.instance_eval(iv) if other && other.instance_variables.include?(iv) r << " #{iv}=" << object_to_s(obj.instance_eval(iv), other.instance_eval(iv)) else r << " #{iv}=" << object_to_s(obj.instance_eval(iv)) end r << "," unless i == vars.size - 1 elsif other r << " ..." end end r << ">" end end end class TC_ObjectDiff < Test::Unit::TestCase def test_to_s object = TestObject1.new('a', 'b', 'c', 'd', [1, 2, 3, 4]) def object.__id__ 0 end assert_equal('#', ObjectDiff.object_to_s(object)) end def test_compare object1 = TestObject1.new('a', 'b', 'c', 'd') def object1.__id__ 1 end object2 = TestObject1.new('a', '', 'c', '') def object2.__id__ 2 end string1, string2 = ObjectDiff.compare(object1, object2) puts [string1, string2] assert_equal('#', string1) assert_equal('#', string2) complexObject1 = TestObject1.new('1', {:a => 1, :b => 2, :j => 3}, [1, 2, 3], object1) def complexObject1.__id__ 3 end complexObject2 = TestObject1.new('1', {:a => 2, :b => object1, :j => 3}, [0, 2], object2) def complexObject2.__id__ 4 end string1, string2 = ObjectDiff.compare(complexObject1, complexObject2) puts [string1, string2] assert_equal('#1, :b=>2, ...}, @c=[1, ... 3], @d=#, ...>', string1) assert_equal('#2, :b=>#, ...}, @c=[0, ...], @d=#, ...>', string2) end def test_compare_different_classes object1 = TestObject1.new('a', 'b', 'c', 'd') def object1.__id__ 5 end object2 = TestObject2.new('a', '', 'c', '', 'f', 'g') def object2.__id__ 6 end string1, string2 = ObjectDiff.compare(object1, object2) puts [string1, string2] assert_equal('#', string1) assert_equal('#', string2) end def test_compare_array a = [1, 2, 3, 4, 5] b = [1, 3, 4, 4, 5, 6] string_a, string_b = ObjectDiff.compare_array(a, b) puts [string_a, string_b] assert_equal('[... 2, 3, ... ...]', string_a) assert_equal('[... 3, 4, ... ... 6]', string_b) end def test_compare_hash a = { :a => 1, :b => 2, :c => 3, :d => 4 } b = { :j => 2, :b => 3, :c => 3, :d => 5 } string_a, string_b = ObjectDiff.compare_hash(a, b) puts [string_a, string_b] assert_equal('{:a=>1, :b=>2, ... :d=>4}', string_a) assert_equal('{:b=>3, ... :d=>5, :j=>2}', string_b) end end --X1bOJ3K7DJ5YkBrT--