undefined method ... for class (NoMethodError) - ruby

I'm just startin to learn ruby and I'm writing a simple program, but I've got an error undefined method 'send_for_beer' for Person:Class (NoMethodError)
Here is a code:
class Person
#iq = 0
#speed = 0
#power = 0
#beauty = 0
def initialize (iq, speed, power, beauty)
#iq = iq
#speed = speed
#power = power
end
def send_for_beer
result #iq * 2 + #speed * 10 + #power * 5 + #beauty
return result
end
end
number_of_people = 3
person_array = Array.new(number_of_people, Person)
n = 0
beer_person = 0
beer_cof = 0
number_of_people.times do
............
person_array.push(Person.new(iq, speed, power, beauty))
if person_array[n].send_for_beer > beer_cof <-----here is an error
beer_cof = person_array[n].send_for_beer
beer_person = n
end
n = n+1
end

Here's your problem:
person_array = Array.new(number_of_people, Person)
In short, don't make array like this. Use the [] literal syntax. What this returns is:
[Person, Person, Person]
That is 3 references to the Person class, not instances. Then later you do:
person_array.push(Person.new(iq, speed, power, beauty))
And you end up with:
[Person, Person, Person, person_instance]
So when you iterate through and call send_for_beer on that first item, it does have that method because send_for_beer is an instance method that you are calling erroneously on a class object.
The fix here is to simply assign person_array to an empty array literal, and then push things to it.
person_array = []
And a minor style note: << is usually preferred to Array#push, making the filling of the array look more like this.
person_array << Person.new(iq, speed, power, beauty)
Ruby also support implicit return of the last expression in a method. So you do not need to return result. Instead, simply calulate the return value as the only line in the method.
def send_for_beer
#iq * 2 + #speed * 10 + #power * 5 + #beauty
end
Instance variables don't quite work like that either. When you have #name in the class body directly, you are not initializing instance variables for each instance. You are actually setting instance variable on the class object (which is weird, I know). What you actually need to do is set them from any instance method, typically initialize, which you are doing here. So you can totally remove the instance variable setting at the class level here.

I think you've a syntax error in the method send_for_beer , the = sign is missing in the affectation of the variable result.
By the way, the method can be written
def send_for_beer
#iq * 2 + #speed * 10 + #power * 5 + #beauty
end

If you have an array of fixed length, you can supply a block to create a new Person object for each element. You could rewrite your person_array line as follows:
person_array = Array.new(number_of_people) { Person.new(0, 0, 0, 0) }
Add the following line to the top of your class.
attr_writer(:iq, :speed, :power, :beauty)
This snipped of code could then modify the objects in your array.
person_array.each do |p|
p.iq, p.speed, p.power, p.beauty = rand(20) + 1, rand(5) + 1, 1
p.beauty = 10 if (rand(2) == 0)
end

Related

Not displaying it's corresponding values with it's key for Hash

Ok i am not here to ask for an answer. But to be honest i am not really good in class variable. So i would appreciate you can guide me along with this piece of code.
I have read on class variable at those docs. I some what kind of understand it. But it comes to applying it for my own use. I would get confused.
class Square
##sqArray = {}
#attr_accessor :length
def initialize
if defined?(##length)
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
else
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
end
end
def Area
##area = ##length * ##length
return ##area
##sqArray[##length.to_sym] = ##area
puts ##sqArray
end
end
s1 = Square.new
puts s1.Area
Let me explain this piece of code. Basically every time i create a Square object it would go to initialize method. A random number will be generated and pass it to ##length, and ##length will be assigned to hash ##sqArray as it's key. But now the problem is when i create a new object s1. When i want to display the Area i want to test out to print the hash ##sqArray with it's length as it's key and area as it's value. But now the problem is only returning it's area only. e.g 114 only.
suppose to be e.g [ 24 => 114]
When defining the object's property (i.e. it's length), the correct approach is to use an instance variable, not a class variable. This is because (in your particular example), length is an attribute of a specific square and not something that applies to all squares. Your code should look something like this:
class Square
def initialize(length = rand(20))
#length = length
end
def area
#length * #length
end
end
s1 = Square.new
puts s1.area
Now, I am a little unclear what exactly you aim to achieve by use of that class variable ##sqArray - but for example, you could use this store a list of all defined Squares:
class Square
##squares_list = []
def self.all_known
##squares_list
end
def initialize(length = rand(20))
#length = length
##squares_list << self
end
def area
#length * #length
end
end
This would allow you to write code like:
s1 = Square.new #=> #<Square:0x0000000132dbc8 #length=9>
s2 = Square.new(20) #=> #<Square:0x000000012a1038 #length=20>
s1.area #=> 81
s2.area #=> 400
Square.all_known #=> [#<Square:0x0000000132dbc8 #length=9>, #<Square:0x000000012a1038 #length=20>]
Class variables have some odd behaviour and limited use cases however; I would generally advise that you avoid them when starting out learning Ruby. Have a read through a ruby style guide to see some common conventions regarding best practice - including variable/method naming (use snake_case not camelCase or PascalCase), whitespace, etc.

Can I implement += in my class to increase a contained value? [duplicate]

This question already has answers here:
Ruby method for +=
(2 answers)
Closed 9 years ago.
Consider
class Container
def initialize(value = 0)
#value = value
end
def + (other)
return #value + other
end
def - (other)
return #value - other
end
def * (other)
return #value * other
end
def / (other)
return #value / other
end
def get
return #value
end
end
I would like to use += to increase the value in the container, like this:
c = Container.new(100)
c += 100
print c.get # Expecting 200
The above won't work, as 100 will overwrite c.
I know I could use something like an attr_accessor to generate a getter and setter for the value, but I'm curious if I could do this in a prettier way such as using +=.
Since c += 100 is just a sugar for c = c + 100, you can't escape overwriting c. BUT you can overwrite it with a similar object (and not with fixnum, as in your question).
class Container
def initialize(value = 0)
#value = value
end
def + (other)
Container.new(#value + other)
end
def get
#value
end
end
c = Container.new(100)
c += 100
c.get # => 200
x += y is just syntactic sugar for x = x + y. So you only have to implement + in your class and you get += for free.
No, you can't overload +=. See list of ruby operators that can be overridden/implemented for the full list of overloadable operators.
In Ruby x += y always means x = x + y. The only way to change the meaning of += for a given x is overriding + in x.class. However, + has a different semantics, and the user most probably expects that + returns a new object. If you make + return the original x, that may confuse some of your users. If you make + return a different object, then x would point to that other object in your example, and as far as I understand your question you don't want that.

Monte Carlo Simulation in Ruby

Hey I have a problem with my simulation.
I am a Ruby-Starter and don't know what's wrong in my code. That is only the part with the simulation:
def mean
mean = self.reduce(:+)/self.length.to_f
return mean
end
def randn
begin
rg1 = (rand*2)-1
rg2 = (rand*2)-1
q = rg1**2 + rg2**2
end while (q == 0 || q > 1)
p = Math.sqrt((-2*Math.log(q))/q)
rn1 = rg1 * p
rn2 = rg2 * p
return rn1, rn2
end
monte_carlo = 10
ren1_sim = Array.new
ren2_sim = Array.new
monte_carlo.times {
(1..20).each{ |i|
(1..250).each { |j|
r = randn()
ren1= * Math.exp(mu_ren1 + sigma_ren1 * r[0])
# ren1 is an array with prices, mu_ren1 and sigma_ren1 are individual values
ren2= * Math.exp(mu_ren2 + chol_21 * r[0] + chol_22 * r[1])
# chol_21 and chol_22 are also individual values
ren1_sim.push(ren1)
ren2_sim.push(ren2)
}
}
}
puts ren1_sim.mean
puts ren2_sim.mean
I don't get an error without the last two puts, but when I want to calculate the average of the arrays ren1_sim and rent_sim I get the error:
undefined method 'mean' for #<Array:0x2acf318> (NoMethodError)
Do you know how to fix that?
You're trying to invoke mean on an Array, which is not a method of Array. Perhaps you meant to use Statsample::Vector, which is Statsample's extension of Array, and does have mean?
ren1_sim = Statsample::Vector.new
ren2_sim = Statsample::Vector.new
You can also call to_vector on an Array instance to get a Statsample::Vector.
You've defined a mean method at the top of your file, but that just creates a method on the top level object, and you're trying to call it on an individual array. You could either change that code to
def mean(array)
array.reduce(:+)/array.length.to_f
end
and then change your usage of it later on to mean(ren1_sim)
or change your code so that you are adding the method to array, i.e.
class Array
def mean
self.reduce(:+)/self.length.to_f
end
end
have a look at this post to calculate the average of a array
How do I create an average from a Ruby array?

Creating objects from YAML, already initialized constant

Two problems, that probably are related:
I'm retreiving a number of 'persons' from a YAML-file to an array, and now i'm trying to create classes from that array.
These objects are then to placed in a new array.
It actually works out fine, if you dont consider the fact that the object added last replaces all the previously added.
In my case i get five identical copies of object #5, where i rather like to see five different ones.
My guess is that the error results somewhere in my iterator to get all the 'persons' from the YAML.
I'm getting a cuople of warnings, regarding the 're-use' of constants:
NEWSTR and NEWAL.
getPost = 0
loopa = 0
while loopa < personsInYAML
NEWSTR = TEST.fetch(getPost)
NEWAL = NEWSTR.split(' ')
getPost+=1
puts "*****************************************"
nyloop = loopa+1
puts "PERSON: " + nyloop.to_s + " name: " + NEWAL.fetch(1)
nameToArray = Person.new
outputArray.insert(loopa, nameToArray)
loopa+=1
end
Persons-class
class Person
def initialize
#name
#age
#length
#weight
#misc
end
def name
name = NEWAL.fetch(1)
return name
end
if NEWAL.include?("age:")
def age
x = NEWAL.index("age:")+1
age = NEWAL.fetch(x)
return age
end
end
if NEWAL.include?("length:")
def length
x = NEWAL.index("length:")+1
length = NEWAL.fetch(x)
return length
end
end
if NEWAL.include?("weight:")
def weight
x = NEWAL.index("weight:")+1
weight = NEWAL.fetch(x)
return weight
end
end
if NEWAL.include?("misc:")
def misc
x = NEWAL.index("misc:")+1
misc = NEWAL.fetch(x)
return misc
end
end
end
You're taking the wrong approach to populating your Person class. The only thing your loop is doing is to create brand new Person classes and stick them in an array. It isn't actually initializing the person class at all.
It looks like what you are trying to do is to use a constant (which you don't hold constant) to pass information to the Person class. However, the code that you have in your Person class that is outside of the methods is only going to be run once - when the class loads for the first time, NOT at the time that you make a new Person.
You'd be better off changing your initialize method to take some arguments, and to create the class with appropriate arguments within the loop.
def initialize(name, age = nil, length = nil, weight = nil, misc = nil)
# assign instance variables here
#name = name
...
end
You appear to be trying to create dynamic accessors to the instance variables. This doesn't make a whole lot of sense. Just define accessors on all of them, and handle the case where the instance variables are nil in whatever code is calling the Person class.

Passing a method as a parameter in Ruby

I am trying to mess around a little bit with Ruby. Therefor I try to implement the algorithms (given in Python) from the book "Programming Collective Intelligence" Ruby.
In chapter 8 the author passes a method a as parameter. This seems to work in Python but not in Ruby.
I have here the method
def gaussian(dist, sigma=10.0)
foo
end
and want to call this with another method
def weightedknn(data, vec1, k = 5, weightf = gaussian)
foo
weight = weightf(dist)
foo
end
All I got is an error
ArgumentError: wrong number of arguments (0 for 1)
The comments referring to blocks and Procs are correct in that they are more usual in Ruby. But you can pass a method if you want. You call method to get the method and .call to call it:
def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
...
weight = weightf.call( dist )
...
end
You want a proc object:
gaussian = Proc.new do |dist, *args|
sigma = args.first || 10.0
...
end
def weightedknn(data, vec1, k = 5, weightf = gaussian)
...
weight = weightf.call(dist)
...
end
Just note that you can't set a default argument in a block declaration like that. So you need to use a splat and setup the default in the proc code itself.
Or, depending on your scope of all this, it may be easier to pass in a method name instead.
def weightedknn(data, vec1, k = 5, weightf = :gaussian)
...
weight = self.send(weightf)
...
end
In this case you are just calling a method that is defined on an object rather than passing in a complete chunk of code. Depending on how you structure this you may need replace self.send with object_that_has_the_these_math_methods.send
Last but not least, you can hang a block off the method.
def weightedknn(data, vec1, k = 5)
...
weight =
if block_given?
yield(dist)
else
gaussian.call(dist)
end
end
...
end
weightedknn(foo, bar) do |dist|
# square the dist
dist * dist
end
But it sounds like you would like more reusable chunks of code here.
You can pass a method as parameter with method(:function) way. Below is a very simple example:
def double(a)
return a * 2
end
=> nil
def method_with_function_as_param( callback, number)
callback.call(number)
end
=> nil
method_with_function_as_param( method(:double) , 10 )
=> 20
The normal Ruby way to do this is to use a block.
So it would be something like:
def weightedknn(data, vec1, k = 5)
foo
weight = yield(dist)
foo
end
And used like:
weightedknn(data, vec1) { |dist| gaussian( dist ) }
This pattern is used extensively in Ruby.
You can use the & operator on the Method instance of your method to convert the method to a block.
Example:
def foo(arg)
p arg
end
def bar(&block)
p 'bar'
block.call('foo')
end
bar(&method(:foo))
More details at http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
You have to call the method "call" of the function object:
weight = weightf.call( dist )
EDIT: as explained in the comments, this approach is wrong. It would work if you're using Procs instead of normal functions.
I would recommend to use ampersand to have an access to named blocks within a function. Following the recommendations given in this article you can write something like this (this is a real scrap from my working program):
# Returns a valid hash for html form select element, combined of all entities
# for the given +model+, where only id and name attributes are taken as
# values and keys correspondingly. Provide block returning boolean if you
# need to select only specific entities.
#
# * *Args* :
# - +model+ -> ORM interface for specific entities'
# - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
# * *Returns* :
# - hash of {entity.id => entity.name}
#
def make_select_list( model, &cond )
cond ||= proc { true } # cond defaults to proc { true }
# Entities filtered by cond, followed by filtration by (id, name)
model.all.map do |x|
cond.( x ) ? { x.id => x.name } : {}
end.reduce Hash.new do |memo, e| memo.merge( e ) end
end
Afterwerds, you can call this function like this:
#contests = make_select_list Contest do |contest|
logged_admin? or contest.organizer == #current_user
end
If you don't need to filter your selection, you simply omit the block:
#categories = make_select_list( Category ) # selects all categories
So much for the power of Ruby blocks.
Similarly to a Proc or a method call, you can also pass a lambda as weightf parameter :
def main
gaussian = -> (params) {
...
}
weightedknn(data, vec1, k = 5, gaussian, params)
# Use symbol :gaussian if method exists instead
end
def weightedknn(data, vec1, k = 5, weightf, params)
...
weight = weightf.call(params)
...
end
you also can use "eval", and pass the method as a string argument, and then simply eval it in the other method.

Resources