Why can't I append an array? - ruby

I want to add user input to an array if it's a palindrome. I keep getting:
undefined method `<<' for nil:NilClass
when I run it.
Eventually I want to compare all values and find the highest, that is why I have the class in there.
class Palindrome
attr_accessor :palindrome_array
#palindrome_array = []
def check_palindromic(variable)
if variable.reverse == variable
#palindrome_array << variable
end
end
end
a = Palindrome.new
puts "Enter a number"
i = gets.chomp()
a.check_palindromic(i)
I have been scratching my head over this. Can someone tell me what is missing?

Because your object a didn't have the instance variable #palindrome_array, that you think it has. You need to do this:
a = Palindrome.new
a.palindrome_array=[]
puts "Enter a number"
i = gets.chomp()
a.check_palindromic(i)
Remove #palindrome_array = [] from the this code as it has different meaning:
class Palindrome
attr_accessor :palindrome_array
#palindrome_array = []
Here is additional explanation:
class Palindrome
attr_accessor :palindrome_array
#palindrome_array = []
def check_palindromic(variable)
if variable.reverse == variable
#palindrome_array << variable
end
end
end
a = Palindrome.new
Palindrome.instance_variables # => [:#palindrome_array]
a.instance_variables # => []
a.palindrome_array=[]
a.instance_variables # => [:#palindrome_array]
I keep getting "undefined method `<<' for nil:NilClass" when I run it.
Humm. This happens because #palindrome_array defined below class Palindrome is an instance variable belonging to the class object. Note this is not the same as a class variable and could not be referred to as ##palindrome_array, whereas #palindrome_array defined inside the check_palindromic method is an instance variable belonging to instances of Palindrome. They are two distinct variables and the first is accessible only in a class method.

You need to initialize all instance variables in your classes constructor:
class Palindrome
...
def initialize
#palindome_array = []
end
...
end
The way you have it currently, you are declaring an instance variable at the class level (which works because all classes inherit from Class)

Related

Ruby - How to access instance variables from classes with "self" methods?

Sorry that I have no clue how to title this, I'm having a hard time looking this up because I don't know how to say this. Anyway...
Let's say I have a class that looks like this for example:
class Run
def self.starting
print "starting..."
end
def self.finished
print "Finished!"
end
end
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting. Now let's say that I wanted to add some instance variables...
class Run
attr_accessor :starting, :finished
def self.starting
print "starting..."
#starting = true
#finished = false
end
def self.finished
print "finished!"
#starting = false
#finished = true
end
end
What if I wanted to access those instance variables from outside the class? I know that something like print "#{Run.finished}" or print "#{Run.starting}" won't do anything. Can I do that without run = Run.new? Or should I just remove self and then use run = Run.new? (Sorry if this question is a mess.)
All of the methods in Run have self before them, meaning that I don't have to do run = Run.new and I can just do Run.starting
There's much more to it than this. In your case you're calling class methods. If you did runner = Runner.new - then you'd be calling instance methods (those are defined without self.
In general, if you need "the thing" to hold some kind of state (like #running = true) then you'd rather want to instantiate an object, and call those methods.
Now, #whatever are instance variables, and you don't have the access to them in class methods.
class Run
attr_reader :running
def start
#running = true
end
def stop
#running = false
end
end
runner = Run.new
runner.running # nil
runner.start
runner.running # true
runner.stop
runner.running # false
I'd recommend you doing some tutorial or basic level book on rails programming, find a chapter about objects and classes. Do some exercises.
In Ruby instance variables are just lexical variables scoped to an instance of a class. Since they are scoped to the instance they always act like a private variable.
If you want to provide access to an instance variable from the outside you create setter and getter methods. Thats what attr_accessor does.
class Person
attr_accessor :name
def initialize(name:)
#name = name
end
def hello
"Hello my name is #{#name}"
end
end
john = Person.new(name: 'John')
john.name = "John Smith"
puts john.hello # "Hello my name is John Smith"
puts john.name # "John Smith"
Methods defined with def self.foo are class methods which are also referred to as singleton methods. You can't access variables belonging to an instance from inside a class method since the recipient when calling the method is the class itself and not an instance of the class.
Ruby also has class variables which are shared by a class and its subclasses:
class Person
##count = 0
def initialize
self.class.count += 1
end
def self.count
##count
end
def self.count=(value)
##count = value
end
end
class Student < Person
end
Person.new
Student.new
puts Person.count # 2 - wtf!
And class instance variables that are not shared with subclasses:
class Person
#count = 0 # sets an instance variable in the eigenclass
def initialize
self.class.count += 1
end
def self.count
#count
end
def self.count=(value)
#count = value
end
end
class Student < Person
#count = 0 # sets its own class instance variable
end
Person.new
Student.new
puts Person.count # 1
Class variables are not used as often and usually hold references to things like database connections or configuration which is shared by all instances of a class.
You can't access instance variables from outside the instance. That is the whole point of instance variables.
The only thing you can access from outside the instance are (public) methods.
However, you can create a public method that returns the instance variable. Such a method is called an attribute reader in Ruby, other languages may call it a getter. In Ruby, an attribute reader is typically named the same as the instance variable, but in your case that is not possible since there are already methods with the names starting and finished. Therefore, we have to find some other names for the attribute readers:
class Run
def self.starting?
#starting
end
def self.finished?
#finished
end
end
Since this is a common operation, there are helper methods which generate those methods for you, for example Module#attr_reader. However, they also assume that the name of the attribute reader method is the same as the name of the instance variable, so if you were to use this helper method, it would overwrite the methods you have already written!
class << Run
attr_reader :starting, :finished
end
When you do this, you will get warnings (you always have warning turned on when developing, do you?) telling you that you have overwritten your existing methods:
run.rb:19: warning: method redefined; discarding old starting
run.rb:2: warning: previous definition of starting was here
run.rb:19: warning: method redefined; discarding old finished
run.rb:5: warning: previous definition of finished was here

How to Initialize Class Arrays in Ruby

I want to create an empty array as a class instance variable in Ruby. However, my current method does not seem to work.
Here is my code:
class Something
#something = []
def dosomething
s = 5
#something << s
end
end
When I call the function, it gives me an undefined method traceback.
However, if I do something similar with class variables, i.e.:
class Something
##something = []
def dosomething
s = 5
##something << s
end
end
This works perfectly.
I know I can use the initialize method to actually create an empty list for #something, but is there another way of doing this without using the initialize method? And why does this work for class variables?
EDIT: Fixed typo
You need to use initialize as a constructor as below code and is there any reason why not to use initialize/constructor. And please fix a typo error in class definition Class Something to class Something no camel case or first letter capitalize while in class
class Something
def initialize
#something = Array.new
end
def dosomething
s = 5
#something << s
end
end
class variable ## are available to the whole class scope. so they are working in the code and if you want to use instance variable # you need to initialize it as above. The instance variable is share with instance/objects of a class
for more details visit the link Ruby initialize method
At first you have a typo. Change Classto class. Next I suggest to use the initialize method. While creating a new object this is the perfect place to initialize instance variables.
class Something
##my_class_variable = [1]
def initialize
#something = []
end
def dosomething
s = 5
#something << s
end
def self.get_my_class_variable
##my_class_variable
end
end
Your script will be read and executed from top to bottom and after this,
you can access the class Something. While the parser reads your script/class/module you can define class variables (##), execute mixins and extend the class with other modules. This is why you can define a class variable, but you can not define an instance variable. Because actually you have no instance object from your class. You only have a class object. In ruby everything is an object. And your class object has a defined class variable now:
Something.get_my_class_variable
# => [1]
Now you can create an instance from your class. With Something.new the initialize method will be invoked and your instance variable will be defined.
something = Something.new
something.dosomething
# => [5]
Later, if you are familar with this you can define getter and setter methods with attr_reader, attr_writer and attr_accessor for instance objects or cattr_reader, cattr_writer and cattr_accessor for class objects. For example:
class Something
attr_reader :my_something
def initialize
#my_something = []
end
def dosomething
s = 5
#my_something << s
end
end
something = Something.new
something.my_something
# => []
something.dosomething
# => [5]
something.my_something
# => [5]
Your problem in trying to access #something in your instance method is that, in the scope of instance methods, # variables refer to instance variables, and your #something is a class instance variable.
# variables are instance variables of the instance that is self when they are created. When #something was created, self was the class Something, not an instance of Something, which would be the case inside an instance method.
How then to access a class instance variable in an instance method? Like regular instance variables, this must be done via a method, as in attr_accessor. One way to do this is to use class << self to tell the Ruby interpreter that the enclosed code should be evaluated with the class (and not the instance) as self:
class C
#foo = 'hello'
class << self
attr_accessor :foo # this will be a class method
end
def test_foo # this is, of course, an instance method
puts self.class.foo # or puts C.foo
end
end
We can show that this works in irb:
2.3.0 :005 > C.foo
=> "hello"
2.3.0 :006 > C.new.test_foo
hello
You have correctly created a class instance variable, #something, and initialized it to an empty array. There are two ways for instances to obtain or change the value of that variable. One is to use the methods Object#instance_variable_get and Object#instance_variable_set (invoked on the class):
class Something
#something = []
def dosomething
s = 5
self.class.instance_variable_get(:#something) << s
end
end
sthg = Something.new
sthg.dosomething
Something.instance_variable_get(:#something)
#=> 5
The other way is to create an accessor for the variable. There are several ways to do that. My preference is the following:
Something.singleton_class.send(:attr_accessor, :something)
Something.something #=> [5]
In your dosomething method you would write:
self.class.something << s

Ruby: Why is an instance variable defined inside a class nil?

class Something
#b = [4432]
def screen
puts #b.class
end
end
s = Something.new
s.screen
outputs 'Nilclass'. Was wondering, why does an instance variable which is defined inside a class always part of NilClass?
Instance variables belong to an object (aka an instance), that's why they are called instance variables. Every instance has its own instance variables.
In your case, there are two objects: Something (which is an instance of Class) and s (which is an instance of Something). Each of those two objects has its own set of instance variables. Something has an instance variable called #b which points to [4432]. s has no instance variable named #b because you never assign to it, and uninitialized instance variables evaluate to nil.
You need to set it like this:
class Something
def initialize
#b = [4432]
end
def screen
puts #b.class
end
end
The way you did it, the variable belongs to Something class itself, not its instance. Observe:
class Something
#b = [4432]
end
s = Something.new
s.instance_variable_get(:#b) # => nil # !> instance variable #b not initialized
Something.instance_variable_get(:#b) # => [4432]
Generally the instance variable must be defined inside the constructor whereas in ruby the default constructor is initialize the syntax is
def initialize
end #these is the default constructor in ruby
so when we define the insatnce variable inside the constructor and when we create the instance of a class then that instance/object will contain the copy of instance variables
most important thing is that though the instance/object contains the instance variable the instance/object cannot access it why because by default the instance data is private so in order to access it we need to define the getters and setter for those instance variable
class Something
attr_accessor:b
def initialize
#b = [4432]
end
s=Something.new
puts"#{s.b}"
Because the variable #b does not exist!. For e.g. the following would produce the same results you see.
class Something
#b = [4432]
def screen
puts #a.class #=> note #a which is non-existent
end
end
s = Something.new
s.screen
Whereas
class Something
#b = [4432]
def screen
puts #a.class
end
def self.screen
puts #b.class
end
end
s = Something.new
s.screen #=> NilClass
Something.screen #=> Array
if you initialize #b outside the initializer, you want #b scope to be a class variable, so you have to call it ##b :
##b has the same value for all Instance of your Something Class
like :
class Somthing
##b = [4432]
def initialize
#[...]
end
def screen
puts ##b.class
end
end
#Jörg W Mittag answer is correct. I just wont to add, that defining an instance variable in class != defining instance variable in an instance of that class. To create an instance variable, in your case in s instance you need to add an initialize method witch gets triggered when new method is called on a class.
def initialize(b_value = default_value)
#b = b_value
end

Ruby and class variables in inherit class

class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.

Dynamically creating class in Ruby

I have a class that should look something like this:
class Family_Type1
#people = Array.new(3)
#people[0] = Policeman.new('Peter', 0)
#people[1] = Accountant.new('Paul', 0)
#people[2] = Policeman.new('Mary', 0)
def initialize(*ages)
for i in 0 ... #people.length
#people[i].age = ages[i]
end
end
end
I want to be able to define a bunch of classes similar to this one at runtime (define them once at startup) where the size of the array and the type assigned to each parameter is defined at runtime from an external specification file.
I sort of got it to work using evals but this is really ugly. Any better way?
From what I understand, you need meta-programming. Here is a snippet of code for creating classes dynamically (on the fly) with initialize method that initializes instance variables-
class_name = 'foo'.capitalize
klass = Object.const_set(class_name,Class.new)
names = ['instance1', 'instance2'] # Array of instance vars
klass.class_eval do
attr_accessor *names
define_method(:initialize) do |*values|
names.each_with_index do |name,i|
instance_variable_set("#"+name, values[i])
end
end
# more...
end
Hope you can tweak it to suit your requirements.
First off, part of the reason your example code isn't working for you is that you have two different #people variables - one is an instance variable and the other is a class instance variable.
class Example
# we're in the context of the Example class, so
# instance variables used here belong to the actual class object,
# not instances of that class
self.class #=> Class
self == Example #=> true
#iv = "I'm a class instance variable"
def initialize
# within instance methods, we're in the context
# of an _instance_ of the Example class, so
# instance variables used here belong to that instance.
self.class #=> Example
self == Example #=> false
#iv = "I'm an instance variable"
end
def iv
# another instance method uses the context of the instance
#iv #=> "I'm an instance variable"
end
def self.iv
# a class method, uses the context of the class
#iv #=> "I'm a class instance variable"
end
end
If you want to create variables one time in a class to use in instance methods of that class, use constants or class variables.
class Example
# ruby constants start with a capital letter. Ruby prints warnings if you
# try to assign a different object to an already-defined constant
CONSTANT_VARIABLE = "i'm a constant"
# though it's legit to modify the current object
CONSTANT_VARIABLE.capitalize!
CONSTANT_VARIABLE #=> "I'm a constant"
# class variables start with a ##
##class_variable = "I'm a class variable"
def c_and_c
[ ##class_variable, CONSTANT_VARIABLE ] #=> [ "I'm a class variable", "I'm a constant" ]
end
end
Even so, in the context of your code, you probably don't want all your instances of Family_Type1 to refer to the same Policemen and Accountants right? Or do you?
If we switch to using class variables:
class Family_Type1
# since we're initializing ##people one time, that means
# all the Family_Type1 objects will share the same people
##people = [ Policeman.new('Peter', 0), Accountant.new('Paul', 0), Policeman.new('Mary', 0) ]
def initialize(*ages)
##people.zip(ages).each { |person, age| person.age = age }
end
# just an accessor method
def [](person_index)
##people[person_index]
end
end
fam = Family_Type1.new( 12, 13, 14 )
fam[0].age == 12 #=> true
# this can lead to unexpected side-effects
fam2 = Family_Type1.new( 31, 32, 29 )
fam[0].age == 12 #=> false
fam2[0].age == 31 #=> true
fam[0].age == 31 #=> true
The runtime initialization can be done with metaprogramming, as Chirantan said, but if you are only initializing a few classes, and you know what their name is, you can also do it just by using whatever you read from the file:
PARAMS = File.read('params.csv').split("\n").map { |line| line.split(',') }
make_people = proc do |klasses, params|
klasses.zip(params).map { |klass,name| klass.new(name, 0) }
end
class Example0
##people = make_people([ Fireman, Accountant, Fireman ], PARAMS[0])
end
class Example1
##people = make_people([ Butcher, Baker, Candlestickmaker ], PARAMS[0])
end
Assuming you want to create different classes per type/array size at runtime:
If (like in Python) a Ruby class is defined when executed (I think it is), then you can do this:
Define your class inside a function. Have the function recieve array size and type as parameters and return the class in its result. That way, you have a sort of class factory to call for each definition in your spec file :)
If on the other hand you want to just initialize #params based on actual data, keep in mind, that Ruby is a dynamically typed language: Just reassign #params in your constructor to the new array!

Resources