i have the following
class test
hash={}
def printHash
puts hash[1]
puts hash[2]
puts hash[3]
end
end
test.new.printHash
this prints:
1
0
1
Why does this happen? how can i test whether or not i have put something in that spot of the hash? or am i missing something
You're well off the mark, but it appears to be doing something because hash is a builtin function which returns a Fixnum hashcode for the object. When you use square brackets on a Fixnum, you get the value of the specific bit. What you want to do is to create an instance variable, which starts with the sigil #. Also, you have to create instance variables within a method, so we'll use the one that's called whenever an object of the class is created, initialize:
class Test
def initialize
#hash = {}
end
def printHash
puts #hash[1]
puts #hash[2]
puts #hash[3]
end
end
Now you'll find this prints nil for all three. To test whether a hash has a value for a specific key, you can use has_key?.
Basically 'hash' is out of scope, what you are referencing in your printHash function is a different object altogether, normally it would be nil (a new unassigned object) but as Pesto points out 'hash' is a built in function - somewhat confusing this explanation.
By putting an '#' sign in front of the your variable and assigning it in the initialize method (which is called after 'new') it becomes available in the entire instance of your object.
Related
I want to create a class instance that has a value such that I can do something like puts a = Example.new(1) where a's value is specified in initialize.
I expect that this is a simple problem since all predefined Ruby classes allow this, but I'm unable to figure out how to do it for my classes.
Class#new and Return Values
Your example doesn't quite work because Ruby treats Class#new as a special case, and is expected to invoke the #initialize method and return an object. If it didn't, calling #new on a class would surprise a lot of people by returning the last evaluation of the initializer from your class, or from Object#new if it's otherwise undefined for your class. In either case, this would violate the principle of least surprise.
However, you can do what you want pretty easily by simply creating an accessor method and then chaining off of Example#new. For example, in Ruby 3.1.0:
class Example
attr_reader :int
def initialize(int) = (#int = int)
end
# prints `1` to STDOUT and assigns the value to *a*,
# but returns nil because you're using Kernel#puts
# which always returns nil
puts a = Example.new(1).int
# shows that the local variable *a* is set to the value
# returned by the Example#int accessor for the class'
# #int instance variable
a
#=> 1
To avoid the confusion of having a nil return value (even though this is expected with Kernel#puts, just change your puts statement to use Kernel#p instead:
p a = Example.new(2).int
#=> 2
Refactoring the Example Class for Older Rubies
If you're using an older Ruby than 3.0, you can't use an endless method or the improved handling for them in Ruby 3.1. The only difference is that rather than an inline method, you need to specify it with the standard def...end syntax, e.g.:
class Example
attr_reader :int
def initialize(int)
#int = int
end
end
Otherwise, the points above are valid as far back as any currently-supported Ruby version.
Given this script
def hash
puts "why?"
end
x = {}
x[[1,2]] = 42
It outputs the following
why?
/tmp/a.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/a.rb:6:in `<main>'
It seems that the hash function defned in the script is overriding Array#hash in that case. Since the return value of my hash method is nil and not an Integer, it throws an exception. The following script seems to confirm this
puts [1,2,3].hash
def hash
puts "why?"
end
puts [1,2,3].hash
The output is
-4165381473644269435
why?
/tmp/b.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/b.rb:6:in `<main>'
I tried looking into the Ruby source code but could not figure out why this happens. Is this behavior documented?
You're not overriding Array#hash, you're shadowing Kernel#hash by creating Object#hash:
puts method(:hash)
def hash
puts "why?"
end
puts method(:hash)
That prints:
#<Method: Object(Kernel)#hash>
#<Method: Object#hash>
Fix it so we can see more:
def hash
puts "why?"
super
end
x = {}
x[[1,2]] = 42
Now the output is:
why?
why?
And no error. Try it with x[[1,2,3,4,5,6,7]] = 42 and you'll instead see why? printed seven times. Once for each array element, since the array's hash method uses the hashes of its elements. And Integer#hash doesn't exist, it inherits its hash method from Object/Kernel, so yours gets used.
This is due to a kind of hack in Ruby top level. Have you ever wondered how this works?
def foo
end
p self
foo
class Bar
def test
p self
foo
end
end
Bar.new.test # no error
How are two totally different objects (main and a Bar) able to call foo like it's a private method call? The reason is because... it is a private method call.
When you define a method at the top level of your Ruby script, it gets included (via Object) in every object. That's why you can call top-level methods like they are global functions.
But why does this break only hash and not other common methods? def to_s;end won't break to_s, for example. The reason is because hash is recursive: most* class implementations ultimately call down to Object#hash for their implementations. By redefining that base case, you break it globally. For other methods like to_s you won't see a global change because it's way up the inheritance chain and doesn't get invoked.
* the only objects this doesn't break are a few literals that probably have hard-coded hash values e.g. [] {} "" true etc.
I'm learning Ruby and made a class to help:
class WhatImDoing
def initialize
puts "not doing anything"
end
end
with the output of:
not doing anything
#<WhatImDoing:0xb74b14e8>
I'm curious, what is the second line all about? Is it a reference location for the WhatImDoing object I created? Can I access objects through this location(like a pointer or something)? Etc... Just trying to get a better understanding of Ruby, in general.
Thanks.
The second line is the output of irb, showing the return value of the last statement.
If you set something equal to that value:
> class WhatImDoing
def initialize
puts "not doing anything"
end
def ohai
puts "Ohai"
end
end
> tmp = WhatImDoing.new
=> #<WhatImDoing:0x5cd5a2a9>
You could use it:
> tmp.ohai
Ohai
If you had a custom to_s it would show that instead:
> class WhatImDoing
def to_s
"#{super} kthxbai"
end
endt
> tmp = WhatImDoing.new
=> #<WhatImDoing:0x3e389405> kthxbai
I'm assuming that was the output of irb. Irb tried to print your object, i.e. convert it to a string. Since you didn't provide a custom to_s ("to string") method, your object inherited this one:
http://ruby-doc.org/core-1.9.3/Object.html#method-i-to_s
Returns a string representing obj. The default to_s prints the object’s class and an encoding of the object id. As a special case, the top-level object that is the initial execution context of Ruby programs returns “main.”
Further digging into the source code reveals that the hexadecimal number is, indeed, the memory address occupied by that object instance. There isn't really anything fancy you can do with that information, in Ruby. It's just a convenient way to generate an unique identifier for an object instance.
Yes, it is reference to the object you are creating. Yes, you can access that object.
I've wrote two ruby files for test
test.rb:
#!/usr/bin/ruby
def foo(bar)
bar['key'] = 'value'
end
def my_print(a)
a.each{|k,v|
puts "#{k} => #{v}"
}
end
test_drive.rb:
#!/usr/bin/ruby
require 'test.rb'
hash_test = Hash.new
foo(hash_test)
my_print(hash_test)
It works as what I expect, the output is
key => value
But when I chanege the test.rb to
#!/usr/bin/ruby
def foo(bar)
pre_defined = {'key' => 'value'}
bar = pre_defined
end
def my_print(a)
a.each{|k,v|
puts "#{k} => #{v}"
}
end
Here I used a pre-defined hash, but now it outputs nothing. The "hash_test" is now an empty hash.
Please illustrate me why indeed does this happen?
Here is the simple answer:
In ruby you pass variables with Objects by reference. When you assign an Object to a variable, the variable does not really contain the Object itself within it. Instead, it contains only a reference to that Object.
It may help for you to start seeing the differences between references and objects in order to understand how sending variables as parameters works: the Object itself does not reside in the variable, the variable is pointing to a reference of the Object in memory, because of this, if one variable points to another object and modifies it, that doesn't mean it modified the Object that it was previously referring to.
The important thing for you to understand is that the bar parameter in the foo method is really only a REFERENCE to the Object in memory, not the Object itself. So if bar once pointed to the Object, but it's now referencing another Object, it will modify what it is referencing, not the previous object it pointed to. Here's the code of the last test.rb commented slightly for you to understand it better:
def foo(bar) # here "bar" points to the same object as "hash_test"
pre_defined = {'key' => 'value'} # here a new object is created and referenced
bar = pre_defined # now "bar" points to a new object and forgets about "hash_test"
end
def my_print(a) # here "a" holds a reference to "hash_test" which is empty
a.each{|k,v| # "a" is empty, so it has nothing to put
puts "#{k} => #{v}"
}
end
I hope this helps. In case you need something a little more detailed:
The reason your last version of test.rb prints an empty hash in test_drive.rb is because the Hash Object that the reference "hash_test" points to is not really being modified at all. Instead the foo method, while initially receiving as a parameter called "bar" a reference of the Hash Object that "hash_test" points to, quickly replaces that reference in "bar" with a brand new reference to a brand new Hash Object that the "pre_defined" variable points to. Now "bar" doesn't point to the same Object as "hash_test" does, and so the Object that "hash_test" points to is never being modified. Thus, the Hash Object that "hash_test" contains is never really populated with anything, and is empty when my_print tries to print it.
That is, because Ruby is pass by value, whereby the reference of an object is passed. It’s a similar behavior of what Java does. That means that you have only a copy of the reference within the foo-method and reassigning it does not change the reference outside of this method.
In more detail:
In your first example you pass the Hash to your foo-method. The reference will be copied, so within the foo-method you have a copy of the reference which points to the same object as 'hash_test', but is not the same one. So calling the Hash methods changes the values of the object outside of the method. But in your second example your are not changing values of the given Hash, you assign a new Hash object to the copy of the method reference, which has no effect to the 'hash_test' reference.
I'm trying to keep a hash local to one function that remembers its state between calls to the function. But I don't know how to declare it without a closure (as some users suggested in a similar thread).
I know C++ more thoroughly than ruby, and in C++, I would have ordinarily used a static local variable, like in the first example here: http://msdn.microsoft.com/en-us/library/s1sb61xd.aspx
I managed to hack something together in ruby using the defined? function:
def func x
if not defined? #hash
#hash = Hash.new
end
if #hash[x]
puts 'spaghetti'
else
#hash[x] = true
puts x.to_s
end
end
func 1
func 1
This prints, the following, which is kind of what I want. The only problem is that #hash can be accessed outside of that function.
1
spaghetti
Is there any "cleaner", more preferred way to declare a variable with this behavior (without a factory)? I was going to create two or three variables like #hash, so I was looking for a better way to express this concisely.
What you're doing is pretty common in Ruby, but also so common you don't need to make a big fuss about it. All #-type instance variables are local to that instance only. Keep in mind "instance" generally refers to an instance of a class, but it can refer to the instance of the class as well.
You can use ## to refer to a class instance variable from the context of an instance, but this tends to get messy in practice.
What you want to do is one of the following.
A variable that persists between method calls, but only within the context of a single object instance:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
#hash ||= { }
if #hash[x]
puts 'spaghetti'
else
#hash[x] = true
puts x.to_s
end
end
A variable that persists between method calls, but only within the context of all object instances:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
##hash ||= { }
if ##hash[x]
puts 'spaghetti'
else
##hash[x] = true
puts x.to_s
end
end
This is usually made cleaner by wrapping the ##hash into a class method. This has the secondary effect of making testing easier:
def self.func_hash
#func_hash ||= { }
end
def func(x)
if self.class.func_hash[x]
puts 'spaghetti'
else
self.class.func_hash[x] = true
puts x.to_s
end
end