Getting (NoMethodError) as if object has not been created - ruby

I'm having difficulty running the following. I get the following error
"': undefined method `name' for nil:NilClass (NoMethodError)"
at the
puts shopper[i].name
line
As if sometimes the object hasn't been created by the time it gets to that line
(0 .. 9).each do |i|
shopper[i] = Shopper.new("Shopper Number-#{i}")
(0 .. 19).each do |j|
r = rand(0 .. (k-1))
if shopper[i].unsuccessful_shopping_trip == true
puts "#{shopper[i].name} has failed to buy 3 times and has decided to go home"
i += 1
break
else
if shopper[i].add_product(shop.remove_product_bought_by_shopper(r)) == false
puts "#{shopper[i].name} has tried to buy #{(shop.products[r]).name} but it is sold out"
j -= 1
else
puts "#{shopper[i].name} has bought #{(shop.products[r]).name}"
end
end
end
puts shopper[i].name
puts shopper[i].shopping_list
total_shopper_net += shopper[i].total_net_value
total_shopper_gross += shopper[i].how_much_spent
total_shopper_product_count += shopper[i].total_product_count
end
Can anyone explain this?

You are manually incrementing i within the each iterator. Any subsequent reference to shopper[i] does not yet exist, since shoppers are created only at the top of the loop.

Related

Return behavior for recursion in Ruby

I was working on a simple recursive method to implement Euclid's algorithm in Ruby, and find myself stuck figuring out how to return the desired value once the base case is reached. Here's what I have to far:
def euclid_alg(larger,smaller)
if larger % smaller == 0 && smaller != 1
return smaller
else
puts 'calling self'
euclid_alg(smaller, (larger % smaller))
puts 'executed section after call'
end
puts "made it here #{smaller} #{larger}"
nil
end
puts euclid_alg(100,15)
And the output:
calling self
calling self
executed section after call
made it here 10 15
executed section after call
made it here 15 100
Note there is no output from "puts euclid_alg(100,15)" which I was expecting to return the greatest common divisor of 100 and 15, 5.
In order to troubleshoot, I replaced the return smaller on line 3 with puts smaller. The new output was:
calling self
calling self
5
made it here 5 10
executed section after call
made it here 10 15
executed section after call
made it here 15 100
The addition of the "made it here 5 10" to the console output makes it clear that the return statement is breaking out of the function call, but not "parent calls."
How can I do recursion better?
Your code is just fine. You are simply missing a return. Note:
def euclid_alg(larger,smaller)
if larger % smaller == 0 && smaller != 1
return smaller
else
puts 'calling self'
return euclid_alg(smaller, (larger % smaller)) # <<<<< Return here
puts 'executed section after call'
end
puts "made it here #{smaller} #{larger}"
nil
end
puts euclid_alg(100,15)

call a method from another file in ruby

Hello I'm new to programming and I started with ruby. I'm trying to do my first program. I found online this code that generate a dice roll
class Die
def initialize(sides)
#sides = sides
end
def generate_die_roll
rand(#sides) + 1
end
def roll(number=1)
roll_array = []
number.times do
roll_array << generate_die_roll
end
total = 0
roll_array.each do |roll|
new_total = total + roll
total = new_total
end
total
end
end
and I would like to use in they way that if the number generated is inferior o equal to another print something otherwise something else.
It's probably very easy but i'm trying in every way and now I will need some help please.
that's my code:
require "./newdado.rb"
energia_vitale = 30
puts "Lancia un dado scrivendo (D) da sommare alla tua Energia Vitale iniziale di #{energia_vitale} punti"
scelta = gets.chomp
case scelta
when "D"
SIX_SIDED_DIE = "#{Die.new(6)}"
values = Array[]
values.push(SIX_SIDED_DIE.roll)
puts values
if values < 2
puts "c"
else puts "b"
end
end
when I run it i receive this error
C:/Users/fix/workspace/D&D Ruby/energia vitale.rb:11:in <main>': undefined methodroll' for "#":String (NoMethodError)
Sorry to bother the community with this beginner problem
Why as string?
this line
SIX_SIDED_DIE = "#{Die.new(6)}"`
should be something like
die = Die.new(6)
then you can do die.roll

ruby - syntax error, unexpected tOP_ASGN

The following is a portion of my code. Any advice is appreciated.
def how_many_guesses?
guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
how_many_guesses? += 1
guess
else
puts "Too low!"
how_many_guesses? += 1
guess
end
end
The error I received was:
syntax error, unexpected tOP_ASGN
how_many_guesses? += 1
I don't understand what unexpected tOP_ASGN means.
As other commented you are trying to assign a value to a function.
You can use some other approaches for example using a class variable, or a setter method.
In the first case you use an instance variable, and you just increment or decrement its value:
class Guess
def initialize
#guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
#guesses += 1
guess
else
puts "Too low!"
#guesses += 1
guess
end
end
end
In this example you can use attr_reader to publish the guesses variable
The other way, more c++ style, is using getter and setter functions:
class Guess
def initialize
#guesses = 0
end
def get_guesses
#guesses
end
def set_guesses(guesses)
#guesses = guesses
end
def how_many_guesses?
guesses = 0
end
def guess
puts "Please type a number between 1 and a 100."
gets.chomp
end
def correct?
if guess == guess
puts "You have already guessed this number, try again!"
guess
elsif guess == random_num
you_are_correct
elsif guess > correct_num
puts "Too high!"
set_guesses(get_guesses + 1)
guess
else
puts "Too low!"
set_guesses(get_guesses + 1)
guess
end
end
end
This is another solution, but it is not recommended in ruby.
EDIT: Some comments included the option of using global variables, just don't!
To elaborate on #Gavriel's answer, functions are singletons, while += is an assignment operator
Your code can be rewritten to basically look like so:
def how_many_guesses?
guesses = 0
end
how_many_guesses? = how_many_guesses? + 1
Which is impossible, as you can not assign to function symbols in this manner. It is possible to redefine a function on the fly due to Ruby's robust metaprogramming capabilities, but that falls far outside the scope of this question.
The easiest way for you to fix it is to do the following:
numGuesses = how_many_guesses?
numGuesses += 1
how_many_guesses? is a function, you can only assign a value to a variable. But maybe you should read a bit more about functions generally, as your code indicates you might need some clarification.
What you probably wanted to do is something like:
$guesses = 0
and
$guesses += 1
You don't need a function here I think.
But the bigger problem will be that every time you wrote down "guess" it will wait for you to type a number.
if guess == guess # here you'll enter 2 numbers
puts "You have already guessed this number, try again!"
guess # here you'll enter another one
elsif guess == random_num # and if the 1st and 2nd numbers were different then you'll be asked another one here...
Option 1:
Replace all how_many_guesses? with $guesses making it a global variable. Define guesses at the top of the file: $guesses = 0
Option 2 (Better):
Put everything in a class and make guesses an instance variable:
class GuessGame
def initialize
#guesses = 0
end
def guess
# ...
end
def correct?
# ...
#guesses += 1
end
end

Ruby does not recognize times method

I wrote a program to simulate the rolling of polyhedral dice but every time I run it I get the error shown below. It displays my prompts and lets me input my numbers, but after that it screws up. I've been trying to find a fix online but I only find people talking about different problems with the times method being undefined. I'm new to Ruby, so any help would be appreciated.
My program:
p = 0
while p < 1
puts "Choose your die type"
die_type = gets.chomp
puts "Choose your number of die"
die_number = gets.chomp
total = 0
i = 1
die_number.times do
random_int = 1 + rand(die_type)
total += random_int
i += 1
end
puts total
end
The error I get:
/dieroll.rb:13: undefined method 'times' for "5":String (NoMethodError)
Change die_number = gets.chomp to die_number = gets.to_i. die_number = gets.chomp, assigns a string to the local variable die_number( Because Kernel#gets gives us a string object). String don't have any method called #times, rather Fixnum class has that method.
Change also die_type = gets.chomp to die_type = gets.to_i, to avoid the next error waiting for you, once you will fix the first one.
1.respond_to?(:times) # => true
"1".respond_to?(:times) # => false
In you case die_number was "5", thus your attempt "5".times raised the error as undefined method 'times' for "5":String (NoMethodError).

trouble with variables in a method

In a previous question ( Is a method in ruby similar to a subroutine?) I asked about methods in Ruby. Now, writing my first ever method, I've clearly run into trouble with the scope of variables. The program below interprets and runs fine when I don't call the method learn. That is, if I remove the call learn(2) in line 33, everything works fine and it doesn't seem to matter that I use various variables (e.g. stimulus[]) both in the main program and in the method. But when I insert the call (and use it by pushing the u key), I get the message below, apparently indicating it's not alright to use stimulus in the method.
brain.rb:26:in `block in learn': undefined local variable or method `stimulus' for main:Object (NameError)
from brain.rb:25:in `each'
from brain.rb:25:in `learn'
from brain.rb:33:in `ucr'
from brain.rb:69:in `<main>'
But I NEED to use it (and brain) there, and with their present values as determined by the main program. All the answers to questions about scope that I've come across seem to go the other way, i.e, problems using variables in a method elsewhere. I thought of making stimulus and brain global, but apparently that is a no-no. How do I tell the method to use variables from the program?
Ps. Once this method works, I will be calling it from six other places in the program.
require 'matrix'
class Matrix
def []=(i, j, x)
#rows[i][j] = x
end
end #code to allow putting individual elements in matrix at i,j
def read1maybe
return $stdin.read_nonblock 1
rescue Errno::EAGAIN
return ''
end # part of code to get keypress
brain= Matrix[ [0,0,0,0,99,0,0,0,0,1,0],
[0,0,0,0,0,99,0,0,0,1,0],
[0,0,0,0,0,0,99,0,0,1,0],
[25,0,0,0,0,0,0,1,-1,1,-99],
[0,23,0,0,0,0,0,1,-1,1,1],
[0,0,24,0,0,0,0,1,-1,1,1],
[0,0,0,22,0,0,0,1,-1,1,1] ]
stimulus=Matrix.column_vector([0,0,0,0,0,0,0,0,0,0,0])
behavior=Matrix.column_vector([0,0,0,0,0,0,0])
t=500 # t=threshold
energy=50
# begin defining behavioral methods
def learn(ix)
for j in (7..10)
if stimulus[j]>0 && brain[ix,j] != 0 && brain[ix,j] < 99 then
brain[ix,j]+=int(0.1 * stimulus[j]) * (99-brain[ix,j])
end # if stim
end # for j
end # learn
def ucr
puts "Show UCR"
learn(2)
end
def positive_fixer
puts "Positive fixer"
end
def negative_fixer
puts "Negative fixer"
end
# end defining behavioral methods
# begin main program
while(energy>0) do
(0..10).each {|n| if stimulus[n,0]>2 then stimulus[n,0]+= -2 else stimulus[n,0]==0 end}
input=false
system 'stty cbreak'
look=0
while look < 40000
q = read1maybe
break if q.length > 0
look +=1
end # while look
case q
when "f" then stimulus[4,0]=9 and puts "Good!"
when "p" then stimulus[5,0]=9 and puts "Bad!"
when "u" then stimulus[6,0]=9
when "l" then stimulus[7,0]=9 and stimulus[8,0]=9 and puts "ight on"
when "c" then stimulus[9,0]=9 and puts " BUZZZ"
input=true
end # case q
system 'stty cooked'
if input==false then (0..3).each { |n| stimulus[n,0]=rand(25)} end
behavior=brain*stimulus
if behavior[0,0] > t then positive_fixer end
if behavior[1,0] > t then negative_fixer end
if behavior[2,0] > t then ucr end
if behavior [3,0] > t then puts "show operant 1" end # and stimulus[10,0]=9
if behavior[4,0] > t then puts "show operant 2" end
if behavior[5,0] > t then puts "show operant 3" end
if behavior[6,0] > t then puts "show operant 4" end
energy += -1
# temp to test development of memory
puts brain[2,9]
end # while energy > 0
puts
puts "It's dead Jim."
# end main program
stimulus and brain are declared outside of the method. You need to pass them in as parameters like so:
def learn(ix, brain, stimulus)
for j in (7..10)
if stimulus[j]>0 && brain[ix,j] != 0 && brain[ix,j] < 99 then
brain[ix,j]+=int(0.1 * stimulus[j]) * (99-brain[ix,j])
end # if stim
end # for j end # l
And edit ucr like so:
def ucr(brain, stimulus)
puts "Show UCR"
learn(2, brain, stimulus)
end
And invoke ucr like ucr(brain, stimulus). See the pattern? You need to add the parameters to the method definitions that use them and then pass them in when invoking the method.

Resources