trouble with variables in a method - ruby

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.

Related

Return few lines back if condition is true

Let's say that I have this simple if-elsif-else block of code.
def
# some code...
input = get.chomp
if input == 1
puts "foo"
elsif input == 2
puts "bar"
else
# exit
end
# some more code...
end
How do I tell program to go back and ask for input again for cases 1 and 2 and continue with code within this method if else is triggered? I do not want to go back to start of the method, instead I just want to back to input variable declaration.
def
# some code...
loop do
input = get.chomp
if input == 1
puts "foo"
break
elsif input == 2
puts "bar"
break
end
end
# some more code...
end
Note: Your two if/elsif conditions will never be satisfied.
# main procedure
# defined here so other functions could be declared after
# the main procedure is called at the bottom
def main
loop do
puts "Insert a number"
input = gets.chomp.to_i
if isValidInput input
puts case input
when 1
"foo"
when 2
"bar"
end
break
end
end #loop
puts "Other code would execute here"
end
# Validity Checker
# makes sure your input meets your condition
def isValidInput(input)
if [1,2].include? input
return true
end
return false
end
main

Ruby Yield and For Loop

I'm working my way through a simple tutorial of each vs for loops in Ruby. This is supposed to be one of the simpler examples but for some reason, I don't understand the interaction between the yield statements and the for loop.
class MyEachThing
def each
yield 1
yield 42
yield 2
yield 42
yield 3
end
end
for i in MyEachThing.new
p i
end
# >> 1
# >> 42
# >> 2
# >> 42
# >> 3
Yield in this next example that I made up makes sense to me:
def calling
p yield(45)
end
calling {|i| i*2}
I just don't get how the first example works. Thank you for the help.
for i in MyEachThing.new
p i
end
is similar to this:
MyEachThing.new.each do |i|
p i
end
which means, you are calling each method on MyEachThing instance and passing i to the block.
And, yield is equivalent to: block.call means, you're calling the block with the passed argument (in this case i).
yield i is equivalent to: block.call(i) and your block is just printing the value of i.

Ruby: Why is 'while (p || (p=exns.shift))' not the same as 'while p ||= exns.shift'?

What's wrong here? I don't see how the two fragments can behave differently, yet they do.
Full code, try with one or the other while:
class T
def initialize
#e=[4,2]
end
def shift
r=#e.shift
puts "[#{r.inspect}]"
r
end
end
exns=T.new
while (p || (p=exns.shift))
#while p ||= exns.shift
puts "p: #{p.inspect}"
p -= 1
p=nil if p<1
puts " #{p.inspect}"
puts "T" if p
end
The 'while (p || (p=exns.shift))' never seems to short-circuit for some reason. I'd like to know the reason.
Your problem is with the letter you chose to name your variable - p is not like other letters (say q), since it is also the name of the Kernel method p():
p
# => nil
q
# NameError: undefined local variable or method `q' for main:Object
for that reason, because you did not define a local variable names p before the while loop, it is actually declared only inside the scope of the loop.
To see how this matters, simply add the following line before the loop:
p = nil
Now, both options act the same.

Rspec test cases not working

I probably have done some silly mistake but by running rake shows this
Due to infinite loop it runs infinitely until i stopped it.
The folder structure follows :
calculator.rb
class Calculator
attr_accessor :result
def initialize
#result = 0.0
end
def add(param)
#result += param
end
def subtract(param)
#result -= param
end
def multiply(param)
#result *= param
end
def divide(param)
#result /= param
end
def cancel
#result = 0.0
end
end
class CommandProcessor
attr_accessor :input
attr_accessor :operation
attr_accessor :calculator
def parser
calculator = Calculator.new
while true
input = gets.to_s.chomp
operation = input.split(' ')[0]
param = input.split(' ')[1]
if operation.eql? 'exit'
exit
elsif operation.eql? 'add'
calculator.add(param.to_f)
puts calculator.result
elsif operation.eql? 'subtract'
calculator.subtract(param.to_f)
puts calculator.result
elsif operation.eql? 'multiply'
calculator.multiply(param.to_f)
puts calculator.result
elsif operation.eql? 'divide'
calculator.divide(param.to_f)
puts calculator.result
elsif operation.eql? 'cancel'
calculator.cancel
puts calculator.result
else
puts "invalid op"
end
end
end
end
command = CommandProcessor.new
command.parser
calculator_spec.rb
require 'spec_helper'
require "calculator.rb"
describe "CommandProcessor" do
it "will exit on input exit" do
#cmd = CommandProcessor.new
#cmd.stub!(:gets).and_return("add 3\n")
#cmd.parser
expect(#cmd.calculator.result).to eq 3
end
end
describe "Calculator" do
it "will add a number" do
calculator = Calculator.new
expect(calculator.add 2).to eq 2.0
expect(calculator.add 2.0).to eq 4.0
end
it "will subtract a number" do
calculator = Calculator.new
expect(calculator.subtract 2).to eq -2.0
expect(calculator.subtract 2.0).to eq -4.0
end
it "will multiply a number" do
calculator = Calculator.new
expect(calculator.multiply 2).to eq 0.0
expect(calculator.multiply 2.0).to eq 0.0
end
it "will divide a number" do
calculator = Calculator.new
expect(calculator.divide 2).to eq 0.0
expect(calculator.divide 2.0).to eq 0.0
end
it "will make result zero on cancel" do
calculator = Calculator.new
calculator.cancel
expect(calculator.result).to eq 0.0
end
end
Have i structured the code wrongly or my test are wrong? The code works fine but not the test cases.
Well the prime culprit for the infinite loop, is the one you've introduced yourself, in CommandProcessor#parser.
I believe the problem is that you are stubbing gets on your object, but actually, it is defined on Kernel, so if you are going to stub it anywhere, that's where. This means that gets is probably returning some sort of nonsense that your parser does not understand, which leads to the infinite loop of invalid op that you see. It would probably be useful to print what the invalid op was in that case (i.e. change the puts "invalid op" to puts "invalid op: #{operation}".)
It is worth noting that even if your stub had worked, and you kept getting add 3 instead of garbage data, your programming would still loop forever, because the parser never receives an exit, which it needs to break from the loop.
In my opinion, this issue is indicative of another problem: You are trying to test two things in one go: The parser, and the IO. I would suggest altering your interface so that your CommandProcessor.parse took a string, parses it, and returns the result from the internal Calculator instance. Then it becomes almost trivial to test, which is what we want (nobody wants to think too hard).

Function calls in hash come up empty in Ruby

I've been sifting through the prior questions and answers on stackoverflow, and I have gotten most of my question figured out. I figured out that I can't place a function call within a hash, without placing it within a proc, or a similar container.
What I'm ultimately trying to do is have a menu displayed, grab user input, and then iterate through the hash, and run the specified function:
def Main()
menu_titles = {"Answer1" => Proc.new{Choice1()}}
Menu(menu_titles)
end
def Choice1()
puts "Response answer"
end
def Menu(menu_titles)
menu_titles.each_with_index do |(key, value),index|
puts "#{index+1}. #{key}"
end
user_input = 0
menu_titles.each_with_index do |(key, value), index|
if index.eql?(user_input)
menu_titles[value]
break
end
end
end
Main()
The issue I'm having right now is that I'm not entering the functions that my hash calls for. Whether I use a return or a "puts", I either get a blank line or nothing at all. If anyone has other recommendations about my code, I'm all ears also. To be honest, I don't like using procs, but that's mostly because I don't entirely know how they work and where to use them.
Right now for my menus I have:
user_input = 1
if user_input == 1
Choice1()
...
end
Here's how I would refactor this:
class Menu
attr_reader :titles
# initialize sets up a hard-coded titles instance variable,
# but it could easily take an argument.
def initialize
#titles = {
"Answer1" => Proc.new{ puts "choice 1" },
"Answer2" => Proc.new{ puts "choice 2" }
}
end
# This is the only public instance method in your class,
# which should give some idea about what the class is for
# to whoever reads your code
def choose
proc_for_index(display_for_choice)
end
private
# returns the index of the proc.
def display_for_choice
titles.each_with_index { |(key,value), index| puts "#{index + 1}. #{key}" }
gets.chomp.to_i - 1 # gets will return the string value of user input (try it in IRB)
end
# first finds the key for the selected index, then
# performs the hash lookup.
def proc_for_index(index)
titles[titles.keys[index]]
end
end
If you're serious about Ruby (or object-oriented programming in general), I would highly recommend learning about the advantages of packaging your code into behavior-specific classes. This example allows you to do this:
menu = Menu.new
proc = menu.choose
#=> 1. Answer1
#=> 2. Answer2
2 #(user input)
proc.call
#=> choice 2
And you could actually run it on one line:
Menu.new.choose.call

Resources