From: Eleanor McHugh Date: 2008-05-08T23:07:52+09:00 Subject: Re: initial stages of scripting On 8 May 2008, at 12:36, Hayden Smith wrote: > Hi Ellie, > Thanks for the suggestions, but as is evident from this code - I > believe - I am a relative newbie and therefore, the logic behind > this - at this point - is beyond me. Oh well, back to the bottom of > the class for me. > Cheers Well I didn't want to ruin your homework assignment for you so I sort of dodged around your actual task. Apologies if the following breaks that rule, but getting the hang of basic loops etc. is a fundamental if you want to really enjoy programming. If I lay out your code to better reflect the logical structure it looks like this: question = 1 unless question == 20 score = 0 number_of_questions = 0 num1 = rand(20) num2 = rand(20) correct_answer = num1 + num2 puts("What is #{num1} + #{num2}/?") user_answer = gets.chomp!.to_i end score += 1 number_of_questions += 1 question += 1 puts("Well done! That is correct.\nYou have answered #{score} out of #{number_of_questions} questions correctly.") end exit Clearly there's either a fragment missing at the very start which would matches with the final 'end' statement, or else your first 'end' statement is an erroneous inclusion. This is one of the reasons for laying out code with clear indentation - it makes the logic flow glaringly obvious. It would make sense to put some kind of loop statement at the start otherwise there will be no repetition. With this inclusion what you have is a classic example of what's known as a sentinel guarded loop. You have a sentinel value (in this case 20) and an accumulator 'question' which changes value with each loop iteration until it reaches a specified value. The way in which you would handle this in a traditional procedural language such as BASIC would be to write a code fragment of this form: FOR QUESTION = 1 TO 20 num1 = RND(20) num2 = RND(20) correct_answer = num1 + num2 REM read the value from the keyboard REM compare the result and do some stuff REM etc. END In Ruby it's more natural for these kinds of problems to use a Range object (because Ruby is object-oriented) and then enumerate across it: questions = 1..20 # this creates an instance of the Range 1 through 20 questions.each do |question| # actions to do for the question asked end but you can also use a more procedural style: questions = 1..20 # this creates an instance of the Range 1 through 20 for question in questions # actions to do for the question asked end and either form would provide the loop that you need to ask the user for their answer to successive questions. If you then study the actions that you're taking in your code and consider the actual logic of what you're trying to achieve you'll notice that the score generated will be incorrect. This is where my suggestion of enumerating a Range object using the inject() method rather that each() becomes relevant: questions = 1..20 # this creates an instance of the Range 1 through 20 questions.inject(0) do |sum, question| #do stuff sum + (some_condition ? 1 : 0) end The 'sum' parameter to the block acts as an accumulator and for each element in the range 'questions' whatever the final expression of the code block evaluates to will be the value stored in 'sum'. Once all elements have been enumerated, the value in 'sum' is then returned by inject() as the value of the expression. So for example: numbers = 1..20 result = numbers.inject(0) do |sum, number| sum + number end would set 'result' to 210, which is the sum of the first twenty integers. The way in which this then applies to your problem: questions = 1..20 # this creates an instance of the Range 1 through 20 correct_answers = questions.inject(0) do |sum, question| #do stuff sum + (condition_for_correct_answer ? 1 : 0) end The use of the ternary logic operator is compact but for more complex behaviour a clearer formulation would be: correct_answers = questions.inject(0) do |sum, question| #do stuff sum + if condition_for_correct_answer ? then # print some stuff 1 else # print some other stuff 0 end end which takes advantage of the fact that 'if' is also an expression and will return a value which can be used directly. In languages where 'if' is a statement (and hence does not return a value itself) the logic would look more like: correct_answers = 0 questions.each do |question| #do stuff if condition_for_correct_answer ? then # print some stuff correct_answers += 1 else # print some other stuff correct_answers += 0 end end which is also valid Ruby. And if that's not baffled you completely, you'll be well on your way to Ruby mastery :) Ellie Eleanor McHugh Games With Brains http://slides.games-with-brains.net ---- raise ArgumentError unless @reality.responds_to? :reason