I wrote a ruby program to read a list of graph edges and produce
an adjacency list using a Hash. The graph edges look like
p12 – p31
I hoped to write this as
nbs = Hash.new([])
while line = gets
if line =~ /p(\d+) – p(\d+)/
u, v = $1.to_i, $2.to_i
nbs[u] << v
nbs[v] << u
end
end
but unfortunately, the default value [] for the Hash is shared by all
elements, rather than copied, as I expected.
I was wondering if this was a conscious design decision, or an oversight.
I finally ended up with the much uglier
nbs = Hash.new()
while line = gets
if line =~ /p(\d+) – p(\d+)/
u, v = $1.to_i, $2.to_i
nbs[u] = [] unless nbs.has_key?(u)
nbs[u] << v
nbs[v] = [] unless nbs.has_key?(v)
nbs[v] << u
end
end
[all code snipped]
but unfortunately, the default value for the Hash is shared by all
elements, rather than copied, as I expected.
I was wondering if this was a conscious design decision, or an oversight.
I finally ended up with the much uglier
Is there another way to avoid the explicit tests?
It’s a common problem (are you listening, Bill :).
I don’t know about the design decision, but can offer a nicer solution. Others
will undoubtably have others; I remember there being some clever ones expressed
in the past, but not what they are
Wednesday, October 09, 2002, 3:15:28 PM, you wrote:
but unfortunately, the default value for the Hash is shared by all
elements, rather than copied, as I expected.
It’s a common problem (are you listening, Bill :).
[all code snipped]
but unfortunately, the default value for the Hash is shared by all
elements, rather than copied, as I expected.
I was wondering if this was a conscious design decision, or an oversight.
I finally ended up with the much uglier
Is there another way to avoid the explicit tests?
It’s a common problem (are you listening, Bill :).
I don’t know about the design decision, but can offer a nicer solution.
Others will undoubtably have others; I remember there being some clever
ones expressed in the past, but not what they are
Anyway:
(hash[key] ||= ) << value
Make sense?
Gavin
This seems similar to the problem with Array.new:
Array.new(5, ) will put the same “” in each cell, so:
a = Array.new(5, )
a[2] << 3
a # [[3],[3],[3],[3],[3]]
If you’re using 1.7?+, you can give a block to the constructor which is
evaluated to get the value of each cell:
Yes, I am listening. Actually the first time I saw the post, it came
to my mind to include this as an addition to item #7 in the
“newcomers” list. I guess I will include the new block feature in 1.7
also (although, still ruby-lang.org lists 1.6.7 as the stable version,
and usually I don’t want to deal with some pre-release versions).
I am still “crippled”; our NNTP server seems still not working
properly. Some messages are missing and the surviving messages seems
to have hours of delay. So I am listening and posting from google,
but then google also has hours of delay, although it seems that all
the messages are complete. Definitely I cannot participate in an
“almost-real-time” mode for a while…
I think this example is equivalent as the code in the original post,
but it seems it does not work for me. Any glitch in the code above?
(I think I have some sense on what’s actually going on, but I want to
imitate the code as in the original post.)
Hi Gavin,
[…]
I am still “crippled”; our NNTP server seems still not working
properly. Some messages are missing and the surviving messages seems
to have hours of delay. So I am listening and posting from google,
but then google also has hours of delay, although it seems that all
the messages are complete. Definitely I cannot participate in an
“almost-real-time” mode for a while…
Regards,
Bill
Heh, try living in Australia. Receive ~80 messages in the morning and a tiny
trickle throughout the day.
nbs = Hash.new()
while line = gets
if line =~ /p(\d+) – p(\d+)/
u, v = $1.to_i, $2.to_i
nbs[u] << v
nbs[v] << u
end
end
although it might seem that the “clue” was that the problem is “a
single array which is shared by many hash elements”, probably the more
immediate problem is actually “the use of hash default value as
(intended) lvalue instead of rvalue”?
I am really curious whether the original poster got “overlapped
arrays” problem , or “nbs.length == 0” problem, or both, when the code
finished executing. This is because although
nbs[12]
may return a non-empty array,
nbs.each_key {|key| puts key}
does not print anything, as nbs.length == 0. Is this a commonly known
Ruby hash feature?
To summarize, it seems that
"Given a right key, nbs may return something, but we cannot know
what those keys are from nbs alone." :)
Maybe we can use something like this for cryptography such as hiding
data in our Ruby code so that the “naughty” user cannot do too much
damage?
I am really curious whether the original poster got “overlapped
arrays” problem , or “nbs.length == 0” problem, or both, when the code
finished executing. This is because although
nbs[12]
may return a non-empty array,
nbs.each_key {|key| puts key}
does not print anything, as nbs.length == 0. Is this a commonly known
Ruby hash feature?
Yes: the presence of a default value (even a non-nil one) is not the
same as the presence of a key/value pair. So nbs is an empty hash
whose default value happens not to be nil.
(Consider the alternative. If setting a default actually populated
the hash, what would hsh.keys consist of?
Probably the problem is not that bad. A hash value can be populated with
the default value only when the hash[key] is called as an rvalue. As an
(imaginary) example:
Probably the problem is not that bad. A hash value can be populated with
the default value only when the hash[key] is called as an rvalue. As an
(imaginary) example:
class Hash
alias :oldget :
def
v = oldget(k)
self[k] = v unless has_key?(k)
v
end
end
It’s a bit too “magic” for my taste. It fights against the notion of
a default, by only defaulting once. So if you change the default
dynamically, hsh[“x”] won’t default to the new default:
puts hsh[“k”] # “a”
…
hsh.default = “b”
puts hsh[“k”] # “a”
David
···
On Thu, 10 Oct 2002, William Djaja Tjokroaminata wrote: