# Trouble with local variables

**URL:** <https://www.ruby-forum.com/t/trouble-with-local-variables/177057>\
**Category:** Ruby\
**Created:** [October 10, 2009, 5:58pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057 "2009-10-10T17:58:15Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 5:58pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/1 "2009-10-10T17:58:15Z")

</div>

Hello everyone!  
I’ve been coding with Ruby for about three days, and I just ran into my  
first big snag. I’ve been working on a quiz, and the first one had  
questions like this:

puts ‘How many classes have a taunt kill?’  
puts ‘A - Three’  
puts ‘B - Four’  
puts ‘C - Five’  
puts ‘D - Six’  
answer = gets.chomp.downcase  
if (answer == ‘d’)  
score = score + 1  
else  
end

But it’s messy, since I have to repeat the code for every question. (I  
know, I know, DRY). With this in mind, I began construction on quiz2,  
and used this as my prototype:

def check  
answer = gets.chomp.downcase  
if (answer == c\_answer)  
score = score + 1  
else  
puts 'The correct answer was ’ + c\_answer + ‘.’  
end  
end  
puts ‘Which is the slowest class?’  
puts ‘A - Pyro’  
puts ‘B - Demoman’  
puts ‘C - Heavy’  
puts ‘D - Soldier’  
c\_answer = ‘c’  
check

I understand the problem (mostly), but I’m looking for a workaround.  
Most of my ‘fixes’ don’t work because of the ‘A, B, C, D’ system; should  
that system be changed?

Sorry for such a long post, I’ve just really gotten into this.

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 6:28pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/2 "2009-10-10T18:28:01Z")

</div>

Severin Newsom wrote:

> Hello everyone!  
> I’ve been coding with Ruby for about three days, and I just ran into my  
> first big snag. I’ve been working on a quiz, and the first one had  
> questions like this:
> 
> puts ‘How many classes have a taunt kill?’  
> puts ‘A - Three’  
> puts ‘B - Four’  
> puts ‘C - Five’  
> puts ‘D - Six’  
> answer = gets.chomp.downcase  
> if (answer == ‘d’)  
> score = score + 1  
> else  
> end
> 
> But it’s messy, since I have to repeat the code for every question. (I  
> know, I know, DRY). With this in mind, I began construction on quiz2,  
> and used this as my prototype:
> 
> def check  
> answer = gets.chomp.downcase  
> if (answer == c\_answer)  
> score = score + 1  
> else  
> puts 'The correct answer was ’ + c\_answer + ‘.’  
> end  
> end  
> puts ‘Which is the slowest class?’  
> puts ‘A - Pyro’  
> puts ‘B - Demoman’  
> puts ‘C - Heavy’  
> puts ‘D - Soldier’  
> c\_answer = ‘c’  
> check
> 
> I understand the problem (mostly), but I’m looking for a workaround.  
> Most of my ‘fixes’ don’t work because of the ‘A, B, C, D’ system; should  
> that system be changed?
> 
> Sorry for such a long post, I’ve just really gotten into this.

read about class variables (or global variables)  
@score

Also it will be cleaner if you pass the answer to chech() tru its  
argument list

def check(answer)

…play around.

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 6:29pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/3 "2009-10-10T18:29:26Z")

</div>

Hi,

Am Sonntag, 11. Okt 2009, 00:58:17 +0900 schrieb Severin Newsom:

> puts ‘A - Pyro’  
> puts ‘B - Demoman’  
> puts ‘C - Heavy’  
> puts ‘D - Soldier’

Maybe you do want to do something like that:

def menu ary  
letter = “A”  
ary.each { |entry|  
puts “#{letter} - #{entry}”  
letter.succ!  
}  
end

menu %w(Pyro Demoman Heavy Soldier)

Bertram

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 6:46pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/4 "2009-10-10T18:46:25Z")

</div>

On Sat, Oct 10, 2009 at 11:58 AM, Severin Newsom  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)wrote:

> answer = gets.chomp.downcase  
> answer = gets.chomp.downcase  
> puts ‘D - Soldier’

#checker  
def correct?(item)  
letters = %w(A B C D E F G)  
puts item[:question] + ‘?’  
item[:choices].each\_with\_index {|choice,i|  
puts “#{letters[i]} - #{choice}”  
}  
return  
item[:solution].call(item[:choices][letters.index(gets.to\_s.strip.chomp.upcase)])  
rescue false  
end

#setup  
correct = 0  
questions = [  
{  
:question =\> “Which is the slowest class”,  
:choices =\> %w(Pyro Demoman Heavy Soldier),  
:solution =\> lambda {|input| input == ‘Heavy’}  
},  
{  
:question =\> “What is the best programming language ever”,  
:choices =\> %w(whitespace ruby),  
:solution =\> lambda {|input| input == ‘ruby’}  
}  
]

# do work

questions.each {|question|  
correct +=1 if correct? question  
}

#results

puts “correct: #{correct}, incorrect: #{questions.size-correct} score:  
#{correct/questions.size.to\_f\*100}%”

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 7:38pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/5 "2009-10-10T19:38:31Z")

</div>

Hi –

On Sun, 11 Oct 2009, Rodrigo B. wrote:

> > puts ‘D - Six’  
> > def check  
> > puts ‘C - Heavy’  
> > read about class variables (or global variables)  
> > @score

@score is an instance variable, not a class or global variable. I  
would definitely encourage everyone to learn about class and global  
variables, but they’re literally about the last thing I would reach  
for in almost any situation. You definitely won’t need them here.

David

–  
The Ruby training with D. Black, G. Brown, J.McAnally  
Compleat Jan 22-23, 2010, Tampa, FL  
Rubyist [http://www.thecompleatrubyist.com](http://www.thecompleatrubyist.com)

David A. Black/Ruby Power and Light, LLC ([http://www.rubypal.com](http://www.rubypal.com))

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 10, 2009, 8:00pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/6 "2009-10-10T20:00:57Z")

</div>

Severin Newsom wrote:

> Hello everyone!  
> I’ve been coding with Ruby for about three days, and I just ran into my  
> first big snag. I’ve been working on a quiz, and the first one had  
> questions like this:
> 
> puts ‘How many classes have a taunt kill?’  
> puts ‘A - Three’  
> puts ‘B - Four’  
> puts ‘C - Five’  
> puts ‘D - Six’  
> answer = gets.chomp.downcase  
> if (answer == ‘d’)  
> score = score + 1  
> else  
> end
> 
> But it’s messy, since I have to repeat the code for every question. (I  
> know, I know, DRY). With this in mind, I began construction on quiz2,  
> and used this as my prototype:
> 
> def check  
> answer = gets.chomp.downcase  
> if (answer == c\_answer)  
> score = score + 1  
> else  
> puts 'The correct answer was ’ + c\_answer + ‘.’  
> end  
> end  
> puts ‘Which is the slowest class?’  
> puts ‘A - Pyro’  
> puts ‘B - Demoman’  
> puts ‘C - Heavy’  
> puts ‘D - Soldier’  
> c\_answer = ‘c’  
> check
> 
> I understand the problem (mostly), but I’m looking for a workaround.  
> Most of my ‘fixes’ don’t work because of the ‘A, B, C, D’ system; should  
> that system be changed?
> 
> Sorry for such a long post, I’ve just really gotten into this.

Hey! Making a quiz is not something I have ever done, but after reading  
your post, this is how I would code it up.

#I’d start by making an array with all the right answers to the quiz  
answer\_key = [‘a’, ‘a’, ‘b’, ‘d’, ‘c’, ‘c’] # for instance

# then after printing the question, as you have done above …

answer = []  
answer.push(gets.chomp.downcase)

#The above places the input into a new element in the array

#Once the quiz is complete we can tally the score like this …

answer\_key.each\_index do |i|  
score ||= 0 # initialize the variable unless it it already initialized  
score +=1 if answer\_key[i] == answer[i]  
end

puts “You got #{score} answers correct!”

# The each\_index method of the array class returns the index number for

# each element of the array so that you can iterate through both arrays

# at once, which is pretty cool, I think.

---

<div class="post-metadata">

**Author:** ![sevhead](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/3ab097/32.png) [@sevhead](https://www.ruby-forum.com/u/sevhead)\
**Post date:** [October 13, 2009, 12:27pm UTC](https://www.ruby-forum.com/t/trouble-with-local-variables/177057/7 "2009-10-13T12:27:35Z")

</div>

Severin Newsom wrote:

> def check  
> answer = gets.chomp.downcase  
> if (answer == c\_answer)  
> score = score + 1  
> else  
> puts 'The correct answer was ’ + c\_answer + ‘.’  
> end  
> end  
> puts ‘Which is the slowest class?’  
> puts ‘A - Pyro’  
> puts ‘B - Demoman’  
> puts ‘C - Heavy’  
> puts ‘D - Soldier’  
> c\_answer = ‘c’  
> check
> 
> I understand the problem (mostly)

I think you’d find it helpful if you post the actual error you get when  
you run this program:

quiz.rb:3:in `check': undefined local variable or method`c\_answer’ for  
main:Object (NameError)  
from quiz.rb:15

This basically tells you the problem: ruby cannot find anything called  
“c\_answer”. This is because c\_answer is a local variable - but methods  
cannot access local variables defined outside them. Every method defined  
with ‘def’ starts with a clean slate as far as local variables are  
concerned. There are very good reasons for this which I won’t go into  
here.

So basically, you either make c\_answer a global variable like $c\_answer  
(bad practice), or pass it in as an argument to the method (good  
practice).

def check(c\_answer)  
answer = gets.chomp.downcase  
if (answer == c\_answer)  
score = score + 1  
else  
puts 'The correct answer was ’ + c\_answer + ‘.’  
end  
end  
puts ‘Which is the slowest class?’  
puts ‘A - Pyro’  
puts ‘B - Demoman’  
puts ‘C - Heavy’  
puts ‘D - Soldier’  
check(‘c’)

This moves you along, but if you enter a right answer you get a new  
error:

quiz.rb:4:in `check': undefined method`+’ for nil:NilClass  
(NoMethodError)  
from quiz.rb:14

This is the same problem, in this case ‘score’ is not accessible inside  
the method. However because you have an assignment to score (score =  
…) a local variable is automatically brought into existence with value  
‘nil’, even though the assignment hasn’t actually executed yet. This is  
why you get a potentially confusing error message. “Undefined method…  
for nil” means you tried to execute nil.something (nil.+ in this case)

Again you have a number of options:

1. Have a global variable $score (bad practice, makes it hard to re-use  
code, non-thread-safe etc)

2. Pass in the old score, and return the new score. This is a  
“functional” programming style, meaning that your function doesn’t  
actually modify any state, but just returns new calculated values.

def check(c\_answer, score)  
answer = gets.chomp.downcase  
if (answer == c\_answer)  
score = score + 1  
else  
puts 'The correct answer was ’ + c\_answer + ‘.’  
end  
return score  
end

score = 0

puts ‘Which is the slowest class?’  
puts ‘A - Pyro’  
puts ‘B - Demoman’  
puts ‘C - Heavy’  
puts ‘D - Soldier’  
score = check(‘c’, score)

puts “Your score is #{score}”

1. Build a class which holds the state for your quiz session. This is  
the “object oriented” style.

class QuizSession  
attr\_accessor :score  
def initialize  
@score = 0  
end  
def check(c\_answer)  
answer = gets.chomp.downcase  
if (answer == c\_answer)  
@score = @score + 1  
else  
puts 'The correct answer was ’ + c\_answer + ‘.’  
end  
end  
end

session = QuizSession.new

puts ‘Which is the slowest class?’  
puts ‘A - Pyro’  
puts ‘B - Demoman’  
puts ‘C - Heavy’  
puts ‘D - Soldier’  
session.check(‘c’)

puts “Your score is #{session.score}”

I have presented these options making the minimum changes to your  
existing code.

You can think of ways of extending the QuizSession object - for example  
loading the list of questions from a file, and including the ability to  
iterate through the questions-and-answers.

At the moment, QuizSession is just a score counter and answer checker,  
but that in itself is a useful function, and a good example of  
encapsulation (except that it uses ‘puts’ and so has some user-interface  
built in as well)

You could consider making the user interface a separate object, so that  
you could plug in a CLI Q&A session, a TK Q&A session, a web-based Q&A  
session etc.

HTH,

Brian.
