Markus said:
My guess is that either++ 1) you will find in answering these questions
that you are really wanting a simple combination of two standard
collections, or 2) you will get tangled in trying to come up with
something consistent, or 3) I will learn something interesting.
Hal wrote:
I fear 1 or 2, but I hope for 3.
Here's a stab at an instance of case #1, the simplest way I could come
up with to model the semantics I think you are looking for (note: this
is _not_ claimed to be the best way to implement the semantics, just the
clearest way to express them):
class An_ordered_hash
include Enumerable
#
# A pair of hashes, indicating for each key both the value paired
# with it and the order in which it was added.
#
def initialize
@ordered = {}
@hash = {}
@stamp = 0
end
def []=(k,v)
@ordered[k] = @stamp += 1
@hash[k] = v
end
#
# Iterators proceed in order-of-insertion
#
def keys_in_insertion_order
keys.sort_by { |k| @ordered[k] }
end
def each(*args,&block)
keys_in_insertion_order.each { |k| block.call(k,@hash[k]) }
end
alias each_pair each
def each_key(*args,&block)
keys_in_insertion_order.each { |k| block.call(k) }
end
def each_value(*args,&block)
keys_in_insertion_order.each { |k| block.call(@hash[k]) }
end
def collect(*args,&block)
keys_in_insertion_order.collect { |k| block.call(k,@hash[k]) }
end
#
# Pass anything we don't understand through to the hash
#
def method_missing(method,*args,&block)
@ordered.send method,*args, &block
@hash.send method,*args, &block
end
#
# A few extensions suggested by the array-like nature of the class
#
def <<(kv_pairs)
kv_pairs.each_pair { |k,v| self[k] = v }
self
end
def +(kv_pairs)
An_ordered_hash.new << self << kv_pairs
end
def |(kv_pairs)
self+kv_pairs
end
def &(kv_pairs)
result = self+{}
each { |k,v| result.delete(k) unless result[k] == kv_pairs[k] }
result
end
end
Since we don't have the syntactic sugar we'd like, I put together
something that at least preserves the meaning & intent of it:
class An_ordered_hash
def to_s
"_(" +collect { |k,v| "#{k}=>#{v}" }.join(')+_(')+')'
end
def to_str
to_s
end
def inspect
to_s
end
end
def _(kv_pairs)
An_ordered_hash.new + kv_pairs
end
This setup gives the following answers to the questions I'd asked
previously (properly translated of course):
p _(1=>2)+_(3=>4) # _(1=>2)+_(3=>4)
p _(1=>2)+_(3=>4)+_(1=>5) # _(3=>4)+_(1=>5)
x = _(1=>2)+_(3=>4)+_(1=>5)
p x[1] # 5
x = _(1=>2) << _(3=>4)
x[3] = 6
p x # _(1=>2)+_(3=>6)
x = _(1=>2) << _(3=>4)
x[3],x[1] = x[1],x[3]
p x # _(3=>2)+_(1=>4)
x = _(1=>2) << _(3=>4)
x << _(7=>8)
p x # _(1=>2)+_(3=>4)+_(7=>8)
x = _(1=>2) << _(3=>4)
x += _(7=>8)
p x # _(1=>2)+_(3=>4)+_(7=>8)
x = _(1=>2) << _(3=>4)
x << _(1=>8)
p x # _(3=>4)+_(1=>8)
x = _(1=>2) << _(3=>4)
x += _(1=>8)
p x # _(3=>4)+_(1=>8)
x = _(1=>2) << _(3=>4)
x << _(7=>8)
p x # _(1=>2)+_(3=>4)+_(7=>8)
x = _(1=>2) << _(3=>4)
x |= _(7=>8) << _(3=> 9)
p x # _(1=>2)+_(7=>8)+_(3=>9)
x = _(1=>2) << _(3=>4)
x &= _(1=>2) << _(3=>9) << _(6=>7)
p x # _(1=>2)
x = _(1=>2) << _(3=>4)
x << _(7=>8)
p x # _(1=>2)+_(3=>4)+_(7=>8)
x = _(1=>2) << _(3=>4)
x[1] = 3
p x # _(3=>4)+_(1=>3)
x = _(1=>2) << _(3=>4)
x[1] += 1
p x # _(3=>4)+_(1=>3)
x = _(1=>'2') << _(3=>'4')
x[1].sub!(/2/,'3')
p x # _(1=>3)+_(3=>4)
x = _(1=>2) << _(3=>4)
x[1] += 1
p x # _(3=>4)+_(1=>3)
x = _(1=>2) << _(3=>4)
x[3] = 6
x[1] = 7
p x # _(3=>6)+_(1=>7)
x = _(1=>2) << _(3=>4)
p x.delete(1) # 2
x[3] = 6
x[1] = 7
p x # _(3=>6)+_(1=>7)
x = _(1=>2) << _(3=>4) << _(1=>5)
p x.delete(1) # 5
x[3] = 6
x[1] = 7
p x # _(3=>6)+_(1=>7)
Does this agree with your intuition/desires? Or am I still at sea?
-- MarkusQ