so I'm doing the Learn Ruby the Hard Way exercises and got stuck on one line. Tried googling and even looking for answers in the Python lessons. But Couldnt find an answer.
My question is: Why does Dict.get_slot have this line(what is it for?): return -1, key, default
The original exercise is here: http://ruby.learncodethehardway.org/book/ex39.html
Thanks guys/gals!
module Dict
def Dict.new(num_buckets=256)
# Initializes a Dict with the given number of buckets.
aDict = []
(0...num_buckets).each do |i|
aDict.push([])
end
return aDict
end
def Dict.hash_key(aDict, key)
# Given a key this will create a number and then convert it to
# an index for the aDict's buckets.
return key.hash % aDict.length
end
def Dict.get_bucket(aDict, key)
# Given a key, find the bucket where it would go.
bucket_id = Dict.hash_key(aDict, key)
return aDict[bucket_id]
end
def Dict.get_slot(aDict, key, default=nil)
# Returns the index, key, and value of a slot found in a bucket.
bucket = Dict.get_bucket(aDict, key)
bucket.each_with_index do |kv, i|
k, v = kv
if key == k
return i, k, v
end
end
return -1, key, default
end
def Dict.get(aDict, key, default=nil)
# Gets the value in a bucket for the given key, or the default.
i, k, v = Dict.get_slot(aDict, key, default=default)
return v
end
def Dict.set(aDict, key, value)
# Sets the key to the value, replacing any existing value.
bucket = Dict.get_bucket(aDict, key)
i, k, v = Dict.get_slot(aDict, key)
if i >= 0
bucket[i] = [key, value]
else
bucket.push([key, value])
end
end
def Dict.delete(aDict, key)
# Deletes the given key from the Dict.
bucket = Dict.get_bucket(aDict, key)
(0...bucket.length).each do |i|
k, v = bucket[i]
if key == k
bucket.delete_at(i)
break
end
end
end
def Dict.list(aDict)
# Prints out what's in the Dict.
aDict.each do |bucket|
if bucket
bucket.each {|k, v| puts k, v}
end
end
end
end
Why does Dict.get_slot have this line(what is it for?):
return -1, key, default
That is a return statement that returns an array of three values. Before executing the return statement, ruby will substitute in the values for the variables key and default, then ruby will gather the three values into an Array and return the array. Here is an example:
def dostuff
key = 'a'
default = 10
return -1, key, default
end
p dostuff
--output:--
[-1, "a", 10]
As the comment says:
# Returns the index, key, and value of a slot found in a bucket.
Response to comment:
A Dict is an array of arrays, e.g.:
[
[200, 'hello'],
[210, 'world'],
]
The first element of the array is an Integer that the real key has been converted to, and the second element is the value.
Dict.get_slot() has two possible return values:
bucket.each_with_index do |kv, i|
k, v = kv
if key == k
return i, k, v #****HERE*****
end
end
return -1, key, default #*****OR HERE****
If the key is found in the Dict, which means the key is equal to the first element of one of the sub arrays, then the first return statement executes, and the index of the subarray is returned along with the elements of the subarray, i.e the key and value. The second return statement doesn't execute.
If the key is not found in the Dict, then the first return statement is skipped and the second return statement executes. In the second return statement, -1 is returned for the index of the subarray. The code could have been written to return nil instead, but in other languages it is common to return -1 when a search does not find an element in an array; you know from the documentation for that method that if the index value is -1, then the search came up empty. The docs would say something like:
Dict.get_slot(): returns a three element array. The first element of the returned array is the index of the subarray containing the key or -1 if no subarray contains the key...
As for the default, ruby Hashes allow you to specify a default value that is returned when you try to retrieve a non-existent key(other languages also provide that feature). That allows you to do things like this:
h = Hash.new(0)
h['a'] = 1
h['b'] = 2
target_keys = %w[a b c] #=>['a', 'b', 'c'] For people who are too lazy to type all those quote marks.
sum = 0
target_keys.each do |target_key|
sum += h[target_key]
end
puts sum #=>3
If you couldn't specify a default value, then nil would be returned when a non-existent key was looked up in the Hash, and this would be the result:
`+': nil can't be coerced into Fixnum (TypeError)
That's because the code tries to add nil into the sum. Of course, you could work around that by testing if h[target_key] is nil before doing the addition, but being able to specify a default makes the code more succinct.
Another default example that is more useful and is very common:
results = Hash.new { |this_hash, key| this_hash[key] = [] }
data = [
['a', 1],
['b', 2],
['a', 3],
['b', 4],
['c', 5],
]
data.each do |(key, val)| #The parentheses cause the the subarray that is passed to the block to be exploded into its individual elements and assigned to the variables.
results[key] << val
end
p results
--output:--
{"a"=>[1, 3], "b"=>[2, 4], "c"=>[5]}
If you couldn't specify a default value, then the following line would cause an error:
results[key] << val
When the key didn't exist in the hash yet, results[key] would return nil, and you can't shove val into nil. Once again, you could work around that by testing for nil and then creating a new key whose value is a new array, and then shoving val into the new array.