On Mon, Aug 14, 2006 at 07:31:05AM +0900, Hal F. wrote:
They’ll certainly be the same order. It would be crazy otherwise.
Not necessarily. It could be that, when calling on them separately,
they are accessed in a different order – and only when using one to
reference the other do their associations come to light. It’s
conceivable where there might come a time when someone decides, for
implementation reasons, that this might be a better way to handle it.
It’s still less surprising for them to be accessed in the same order,
though, so it’s nice that it’s implemented this way in absence of a
compelling reason to do otherwise.
This perhaps should be mentioned somewhere, but maybe people
take it for granted.
I suspect people don’t even get as far as taking it for granted, since
they probably tend to access one from the other in almost all cases.
On Mon, Aug 14, 2006 at 07:31:05AM +0900, Hal F. wrote:
They’ll certainly be the same order. It would be crazy otherwise.
Not necessarily. It could be that, when calling on them separately,
they are accessed in a different order – and only when using one to
reference the other do their associations come to light. It’s
conceivable where there might come a time when someone decides, for
implementation reasons, that this might be a better way to handle it.
It’s still less surprising for them to be accessed in the same order,
though, so it’s nice that it’s implemented this way in absence of a
compelling reason to do otherwise.
They would be the same order so long as no changes have been made to
the internal hash table. So, “yes unless you modify the hash or
you’re in threaded code which might modify the hash.”
I suspect people don’t even get as far as taking it for granted, since
they probably tend to access one from the other in almost all cases.
On the second week of my Ruby “experience” I found myself writing a
method (for a very basic db layer) where hash.keys and hash.values could
come in play.
So I think it’s a natural need. After a short Google session I found
others dealing with this, most of them assumed that they have same
order.
Based on the answers of this thread I believe they are, however until it
becomes documented I will use hash.to_a.transpose in production
environment.
On Tue, Aug 15, 2006 at 12:06:51AM +0900, Mage wrote:
Chad P. wrote:
I suspect people don’t even get as far as taking it for granted, since
they probably tend to access one from the other in almost all cases.
On the second week of my Ruby “experience” I found myself writing a
method (for a very basic db layer) where hash.keys and hash.values could
come in play.
So I think it’s a natural need. After a short Google session I found
others dealing with this, most of them assumed that they have same order.
I stand corrected.
Based on the answers of this thread I believe they are, however until it
becomes documented I will use hash.to_a.transpose in production environment.
Here’s another solution - maybe it’s even more efficient as
it doesn’t need the transposing:
Well, a quick benchmark… No, it isn’t more efficient. Neither
time-wise, nor memory-wise.
Your test doesn’t prove higher memory usage for the inject version. The
problem with the transpose approach is that you need an additional copy
of the complete hash in mem which is not needed for inject. However,
inject version is likely to cause more GC because of all the two element
arrays created. You could retest this:
case ARGV.shift
when “transpose”
bm do
1000.times do
keys, values = hash.to_a.transpose
end
end
when “inject”
bm do
1000.times do
keys, values = hash.inject([[],[]]){|(ks,vs),(k,v)| [ks <<
k, vs << v]}
end
end
else
raise “uh?”
end
PS: The circumstances are a bit different, compared to my
previous post. Different environment, different numbers.
$ cat test.rb
require “ev/ruby”
GC.disable
hash = {}
1000.times do |n|
hash[n] = n*n
end
case ARGV.shift
when “traditional”
bm do
1000.times do
keys = hash.keys
values = hash.values
end
end
when “transpose”
bm do
1000.times do
keys, values = hash.to_a.transpose
end
end
when “inject”
bm do
1000.times do
keys, values = hash.inject([[],[]]){|(ks,vs),(k,v)| [ks <<
k, vs << v]}
end
end
when “inject2”
bm do
1000.times do
keys, vals = [], []
hash.each {|k,v| keys << k; vals << v}
end
end
else
raise “uh?”
end
puts meminfo
$ ruby test.rb traditional
VmSize: 14296 kB
CPU ELAPSED COUNT CPU/COUNT LABEL
0.300000 0.310413 1 0.300000 “test.rb:13”
Remember, both to_a and transpose are implemented in C.
Yes, right. Still some remarks
by not using GC you will see the memory used in total but this does
not give a realistic result because it omits GC times and it does make a
difference whether you only allocate small chunks and release them again
or whether you need a copy of the whole data structure in memory, so
memory wise approach “each” is the most efficient
“traditional” is not semantically identical to the other approaches
because it’s not guaranteed that both are in the same order