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:
-
Have a global variable $score (bad practice, makes it hard to re-use
code, non-thread-safe etc)
-
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}”
- 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.