From: Robert Klemme Date: 2005-05-03T01:04:25+09:00 Subject: Re: collections with values of fixed classes/lengths "Eric Mahurin" schrieb im Newsbeitrag news:20050502150819.69075.qmail@web41102.mail.yahoo.com... > > > I still think a more general extension would be nice. And > > > Robert, I guess you might say I'm guessing. I'm new to > > ruby, > > > but from my perl usage, I know of many times I've dealt > > with > > > large amounts of data and would have wanted more C-like > > > efficiency. > > > > ;-) > > > > As Mark has demonstrated, you can stuff anything into a > > String with pack and > > unpack. > > > > Btw, Mark, if you factor out conversion to string and from > > string (hint > > traits), you have a generic implementation for any fixed size > > type. Maybe > > this should go somewhere into the std lib... > > That would be great. What do you mean by "fixed size type"? > Does that mean you couldn't use this class to make a > homogeneous array of strings, arrays, or hashes? Exactly. Because for that to work you need info about each instance stored there. And then you have Array. > The way I was > thinking you'd handle the complex object case would be to give > "new" an initial object instead of a simply a class. From > this, you could tell how much flattening to do in the array. > > Now that I'm looking at Array.new, you could even use the same > interface: > > HomogeneousArray.new(size=0,defaultObject=nil) > > Now, defaultObject is not only the default but its class is > used to make the array homogeneous (if not nil) > > Here are a few examples: > > # like Array.new - objects can be anything (treat nil as Object > instead of NilClass) and initialized to nil (when array > expands) > HomogeneousArray.new(0,nil) This doesn't make sense as that would a) not work in the general case and b) if it would, it would create unnecessary overhead. > # array of Floats initialized to 0.0 > HomogeneousArray.new(0,0.0) > > # array of FixNums initialized to 0 > HomogeneousArray.new(0,0) > > # array of Strings initialized to "" > HomogeneousArray.new(0,"") > > # array of name/address/zip structs where fields can be > anything and are initialized to nil > Customer = Struct.new(:name,:address,:zip) > HomogeneousArray.new(0,Customer.new) > > # now name/address/zip must be String/String/Fixnum and are > initialized to empty strings and 0. There is one more level of > flattening in the array storage compared to above > Customer = Struct.new(:name,:address,:zip) > HomogeneousArray.new(0,Customer.new("","",0)) > > I think having something similar for a Hash would also be > useful. The "new" method with this homogenous hash class just > needs one more optional argument: > > HomogeneousHash.new(defaultValue=nil, defaultKey=nil) > > Here would be a few examples: > > HomogeneousHash.new(nil,nil) # same as Hash.new > HomogeneousHash.new(nil,"") # keys must be Strings > > # keys are Strings and values are String,Fixnum Structs > CustomerData = Struct.new(:address,:zip) > HomogeneousHash.new(CustomerData.new("",0),"") > > # values must be true. No value storage should be necessary > since only one value is possible. Only key storage should be > needed. This acts like an unordered set. > HomogeneousHash.new(true,nil) > > > In addition to all this, some way to designate fixed-length > Arrays, Strings, and Bignums (down to 1-bit) in the collection > values would allow one more level of flattening in the storage. > You could have special classes or just designate a non-zero > length to mean the objects should have a fixed length. For > Bignums/Fixnums, you'd just strip off the most significant 1 to > allow easy specifying of bits and allow any default. Here > would be some more examples using this method for designating > fixed-length Arrays, String, and Bignums: > > # array of 64-bit integers with initial value of 0 > HomogeneousArray.new(0,2**64) > > # array of 16-bit integers with initial value of 1 > HomogeneousArray.new(0,2**16+1) > > # array of 1-bit integers with initial value of 0 > HomogeneousArray.new(0,2**1) > > # array of array of 4 8-bit integers initialized to 0 > HomogeneousArray.new(0,[2**8]*4) > > # array of 2 character string initialized to \0\0 > HomogeneousArray.new(0,"\0\0") > > # array of array of 4 Objects initialized to nil > HomogeneousArray.new(0,[nil]*4) > > > Well, I think this summarizes my proposal. Maybe an RCR is in > order. Personally I don't like the sample instance stuff. This puts too much knowlege into HomogeneousArray while restricting extensibility at the same time. I prefer the traits approach: # disclaimer: this is just a quick demo class HomogeneousArray class FloatTraits def size() 4 end def to_native(str) str.unpack("f*") end def to_string(*values) values.pack("f*") end end FLOAT = FloatTraits.new class IntTraits def size() 4 end def to_native(str) str.unpack("i*") end def to_string(*values) values.pack("i*") end end INT = IntTraits.new include Enumerable def initialize(traits) @traits = traits @storage = "" end def <<(o) @storage << @traits.to_string(o); self end def size() @storage.length / @traits.size end def empty?() @storage.empty? end def each size.times {|i| yield self[i]} self end def [](idx,len=nil) if len @traits.to_native( @storage[idx * @traits.size, len * @traits.size] ) elsif Range === idx @traits.to_native( @storage[idx.first * @traits.size, (idx.last - idx.first) * @traits.size] ) else @traits.to_native( @storage[idx * @traits.size, @traits.size] )[0] end end def to_s() @traits.to_native( @storage ).join end def inspect() @traits.to_native( @storage ).inspect end end hf = HomogeneousArray.new HomogeneousArray::FLOAT hf << 1.2 << 3.4 << 1.2 hi = HomogeneousArray.new HomogeneousArray::INT hi << 1 << 2 << 3 Kind regards robert