# Trouble with local variables

**URL:** <https://rubytalk.org/t/trouble-with-local-variables/55753>\
**Category:** ruby-talk\
**Created:** [10 October 2009 15:58 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753 "2009-10-10T15:58:17Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Severin\_Newsom](https://avatars.discourse-cdn.com/v4/letter/s/7993a0/32.png) [@Severin\_Newsom](https://rubytalk.org/u/Severin_Newsom)\
**Post date:** [10 October 2009 15:58 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/1 "2009-10-10T15:58:17Z")

</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')  
&nbsp;&nbsp;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)  
&nbsp;&nbsp;score = score + 1  
else  
&nbsp;&nbsp;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.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

---

<div class="post-metadata">

**Author:** ![Bermejo\_Rodrigo](https://avatars.discourse-cdn.com/v4/letter/b/e56c9b/32.png) [@Bermejo\_Rodrigo](https://rubytalk.org/u/Bermejo_Rodrigo)\
**Post date:** [10 October 2009 16:28 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/2 "2009-10-10T16:28:03Z")

</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')  
> &nbsp;&nbsp;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)  
> &nbsp;&nbsp;score = score + 1  
> else  
> &nbsp;&nbsp;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.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Bertram\_Scharpf](https://avatars.discourse-cdn.com/v4/letter/b/43a26b/32.png) [@Bertram\_Scharpf](https://rubytalk.org/u/Bertram_Scharpf)\
**Post date:** [10 October 2009 16:28 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/3 "2009-10-10T16:28:53Z")

</div>

Hi,

> puts 'A - Pyro'  
> puts 'B - Demoman'  
> puts 'C - Heavy'  
> puts 'D - Soldier'

Maybe you do want to do something like that:

&nbsp;&nbsp;def menu ary  
&nbsp;&nbsp;&nbsp;&nbsp;letter = "A"  
&nbsp;&nbsp;&nbsp;&nbsp;ary.each { |entry|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "#{letter} - #{entry}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;letter.succ!  
&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;end

&nbsp;&nbsp;menu %w(Pyro Demoman Heavy Soldier)

Bertram

> **···**
>
> Am Sonntag, 11. Okt 2009, 00:58:17 +0900 schrieb Severin Newsom:
> 
> --  
> Bertram Scharpf  
> Stuttgart, Deutschland/Germany  
> [http://www.bertram-scharpf.de](http://www.bertram-scharpf.de)

---

<div class="post-metadata">

**Author:** ![List.rb](https://avatars.discourse-cdn.com/v4/letter/l/f08c70/32.png) [@List.rb](https://rubytalk.org/u/List.rb)\
**Post date:** [10 October 2009 16:45 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/4 "2009-10-10T16:45:49Z")

</div>

#checker  
def correct?(item)  
&nbsp;&nbsp;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 = [  
&nbsp;&nbsp;{  
&nbsp;&nbsp;&nbsp;&nbsp;:question =\> "Which is the slowest class",  
&nbsp;&nbsp;&nbsp;&nbsp;:choices =\> %w(Pyro Demoman Heavy Soldier),  
&nbsp;&nbsp;&nbsp;&nbsp;:solution =\> lambda {|input| input == 'Heavy'}  
&nbsp;&nbsp;},  
&nbsp;&nbsp;{  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:question =\> "What is the best programming language ever",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:choices =\> %w(whitespace ruby),  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:solution =\> lambda {|input| input == 'ruby'}  
&nbsp;&nbsp;}  
]

# do work  
questions.each {|question|  
&nbsp;&nbsp;correct +=1 if correct? question  
}

#results

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

> **···**
>
> On Sat, Oct 10, 2009 at 11:58 AM, Severin Newsom \<severin.newsom@gmail.com\>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.  
> > --  
> > Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Greg\_Barozzi](https://avatars.discourse-cdn.com/v4/letter/g/f6c823/32.png) [@Greg\_Barozzi](https://rubytalk.org/u/Greg_Barozzi)\
**Post date:** [10 October 2009 18:00 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/5 "2009-10-10T18:00:59Z")

</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')  
> &nbsp;&nbsp;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)  
> &nbsp;&nbsp;score = score + 1  
> else  
> &nbsp;&nbsp;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.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://rubytalk.org/u/Brian_Candler)\
**Post date:** [13 October 2009 10:27 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/6 "2009-10-13T10:27:39Z")

</div>

Severin Newsom wrote:

> def check  
> answer = gets.chomp.downcase  
> if (answer == c\_answer)  
> &nbsp;&nbsp;score = score + 1  
> else  
> &nbsp;&nbsp;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)  
&nbsp;&nbsp;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)  
&nbsp;&nbsp;score = score + 1  
else  
&nbsp;&nbsp;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)  
&nbsp;&nbsp;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)  
&nbsp;&nbsp;answer = gets.chomp.downcase  
&nbsp;&nbsp;if (answer == c\_answer)  
&nbsp;&nbsp;&nbsp;&nbsp;score = score + 1  
&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;puts 'The correct answer was ' + c\_answer + '.'  
&nbsp;&nbsp;end  
&nbsp;&nbsp;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}"

3. Build a class which holds the state for your quiz session. This is  
the "object oriented" style.

class QuizSession  
&nbsp;&nbsp;attr\_accessor :score  
&nbsp;&nbsp;def initialize  
&nbsp;&nbsp;&nbsp;&nbsp;@score = 0  
&nbsp;&nbsp;end  
&nbsp;&nbsp;def check(c\_answer)  
&nbsp;&nbsp;&nbsp;&nbsp;answer = gets.chomp.downcase  
&nbsp;&nbsp;&nbsp;&nbsp;if (answer == c\_answer)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@score = @score + 1  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts 'The correct answer was ' + c\_answer + '.'  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;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.

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![David\_A\_Black1](https://avatars.discourse-cdn.com/v4/letter/d/bcef8e/32.png) [@David\_A\_Black1](https://rubytalk.org/u/David_A_Black1)\
**Post date:** [10 October 2009 17:37 UTC](https://rubytalk.org/t/trouble-with-local-variables/55753/7 "2009-10-10T17:37:39Z")

</div>

Hi --

> **···**
>
> On Sun, 11 Oct 2009, Rodrigo Bermejo wrote:
> 
> > 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')  
> > > &nbsp;&nbsp;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)  
> > > &nbsp;&nbsp;score = score + 1  
> > > else  
> > > &nbsp;&nbsp;puts 'The correct answer was ' + c\_answer + '.'  
> > > 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
> 
> @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))
