Not fully understanding the code in the example in "Learn to Program"

So I am not getting similar code to what Chris is when I do the examples he wants us to do, and many of the things he brings up after Chapter 6 seem to be lacking some explanation to create a full understanding so I can fully absorb the concept.

Everything in previous chapters, we would get similar looking stuff. It’s only now that we’ve gotten to branching and looping, and everything afterward that things completely fall apart for me.

Deaf Grandma

My code

puts ''
puts ''

gma = 'Grandma replies: HUH?! SPEAK UP, SONNY!'

year = ''

year_roll = rand(20)

gma_canhear = 'Grandma replies: NO, NOT SINCE ' + year + '!'

if year_roll == 0
	year = '1930'
	elsif year_roll == 1
		year = '1931'
		elsif year_roll == 2
			year = '1932'
			elsif year_roll == 3
				year = '1933'
				elsif year_roll == 4
					year = '1934'
					elsif year_roll == 5
						year = '1935'
						elsif year_roll == 6
							year = '1936'
							elsif year_roll == 7
								year =  '1937'
								elsif year_roll == 8
									year =  '1938'
									elsif year_roll == 9
										year =  '1939'
										elsif year_roll == 10
											year = '1940'
											elsif year_roll == 11
												year =  '1941'
												elsif year_roll == 12
													year = '1942'
													elsif year_roll == 13
														year = '1943'
														elsif year_roll == 14
															year = '1944'
															elsif year_roll == 15
																year = '1945'
																elsif year_roll == 16
																	year = '1946'
																	elsif year_roll == 17
																		year = '1947'
																		elsif year_roll == 18
																			year = '1948'
																			elsif year_roll == 19
																				year = '1949'
																				else year_roll == 20
																					year = '1950'																					
end

gma_canhear = 'Grandma replies: NO, NOT SINCE ' + year + '!' 
#Update gma_canhear variable with year value after the year_roll if branch.

wistg = 'a' 
# what I say to grandma = wistg | This is it's inital value before being updated. If I left it blank it 
# would cause an error. I don't know why.

while wistg != wistg.upcase
	puts 'You say to Grandma...'
	wistg = gets.chomp
	puts ''
		if wistg == wistg.upcase
			puts ''
			puts gma_canhear
			puts ''
			elsif wistg == 'BYE'
				break
				else 	puts gma
						puts ''
			end
end

puts ''
puts ''

Chris’ code

puts 'HEY THERE, SONNY! GIVE GRANDMA A KISS!'
while true
said = gets.chomp
if said == "BYE"
puts 'BYE SWEETIE!'
break
end
if said != said.upcase
puts 'HUH?! SPEAK UP, SONNY!'
else
random_year = 1930 + rand(21)
puts 'NO, NOT SINCE ' + random_year.to_s + '!'
end
end

or

puts 'HEY THERE, SONNY! GIVE GRANDMA A KISS!'
while true
said = gets.chomp
break if said == "BYE"
response = if said != said.upcase
'HUH?! SPEAK UP, SONNY!'
else
"NO, NOT SINCE #{rand(1930..1950)}!"
end
puts response
end
puts 'BYE SWEETIE!'

His code is vastly more compact. I just don’t understand how he was able to do that. I barely understand what he did.

I did what I could, with what I learned form the book up to that point and never in my wildest dreams would I have come up with anything like what he did.

===

2 Likes

Firstly, well done for writing what you did! I wouldn’t worry too much about it being different to what Chris wrote because often programmers will quite write something just to get things working, then they’ll rewrite (refactor) their code to make it better. That could be to make it shorter, or to reorganise or split it up into multiple methods to make it easier to maintain and re-use etc. The thing to note is that this is completely normal - what you end up with is often quite different to your first attempt :smiley:

Ok let’s have a look at Chris’s code:

puts 'HEY THERE, SONNY! GIVE GRANDMA A KISS!'
while true
  users_input = gets.chomp
  if users_input == "BYE"
    puts 'BYE SWEETIE!'
    break
  end
  if users_input != users_input.upcase
    puts 'HUH?! SPEAK UP, SONNY!'
  else
    random_year = 1930 + rand(21)
    puts 'NO, NOT SINCE ' + random_year.to_s + '!'
  end
end

I have changed it very slightly - I changed the name of the said variable, to users_input.

The program starts by printing “HEY THERE, SONNY! GIVE GRANDMA A KISS!” and then waits for user input with the gets.chomp command. By prefixing it with users_input = means the result of it will be bound to the users_input variable - which means you can then use/do something with the result.

Which is exactly what you do with the code below it; if users_input == "BYE" this is what you’ll use to terminate the program, so it needs to be assessed before anything else. If the user’s input is ‘BYE’ the program terminates by first saying “BYE SWEETIE!” then break.

If the user’s input isn’t “BYE” then that bit of code is ignored and the program moves on to the next bit of code: if users_input != users_input.upcase and what that’s doing is asking the computer to check whether the user’s input is in capital letters, which is what upcase is used for. The != bit means ‘does not’ so that full line of code is saying: let’s compare the user’s input to what we want it to to be - the user’s input in capitol letters, so for example if the user’s input was “please give me a kiss” and what we wanted was “PLEASE GIVE ME A KISS” then they would not be equal, making the if users_input != users_input.upcase statement true, which would then execute the code in that block, which is puts 'HUH?! SPEAK UP, SONNY!'.

The final part of the code will run if the user’s input is in capital letters, and in that case the program will first create a random year by choosing a random number from 0 to 21 with the rand(21) function and assign that year to the random_year variable, and then print “NO, NOT SINCE” along with the random_year converted to a string with the to_s method (followed by an exclamation mark).

Does that make sense? If so here’s what I recommend you do - change it! Alter the code to do something a little bit different. When you start doing that it will make even more sense!

Btw, I’ve split this into a dedicated thread as it could help someone else stuck on the same exercise :+1:

3 Likes

I see.

It never occurred to me that I could just use integers, do some simple math, assign them to a variable, and then convert that variable into a string using the .to_s method. I instead did it the more complicated way of trying to get a random number from strings and created more work than was needed. *facepalm.

My approach was flawed because I was working with the years as strings rather than integers which I could do simple math on and just attach that result to a variable and convert the variable to a string using the .to_s method.

===

When you make a loop that says… while true… With no conditional, how does that work? Wouldn’t it just keep looping or never loop?

And is true here, in the example, the Ruby object true or a variable called true? This is confusing for me. How can you tell the difference? Is it simply because we never initialized the variable true so we automatically assume it’s going to be accepted by the Ruby interpreter as an object?

I can understand my code…

while wistg != wistg.upcase

While variable wistg is different from variable wistg all caps do this…

I do not understand how…

while true

… works as it has no conditional.

Thank you for your time.

1 Like

It would… and that’s why the first condition you have checks to see whether it should break or not :smiley: (alternatively you could just quit the IRB session and that would end the program).

It is the Ruby object true - it’s not a variable because a) it’s a protected keyword (so you can’t use it as a variable) and b) even if you could, you did not assign it in your program :smiley:

A neat trick in finding what you’re dealing with is to add .class on the end of it. So in IRB you could assign “hello” to a variable called something then run something.class and it will tell you it’s a string :slight_smile:

And don’t beat yourself up about going the long way round - I can assure you we’ve all done something similar :laughing: and we all have to look up things all the time so don’t worry about having to remember everything either. The way I learn any new thing is to first read as much as I can about it so that I have a fairly good idea of what’s possible, and then when I need to actually use anything I learned I just look up when needed. Investing the time now to read as much as you can before you’re eager to get stuck into a project will save you loads of time in the long run, imo anyway.

Good luck - I’m excited for you! I really loved that book as it made me think I could actually code (Thank you Chris if you are reading this!!)

1 Like

First off I want to thank you so much AstonJ, as you have really helped me out and I greatly appreciate the help.

===

So I can make a loop without immediately specifying the conditional at the start of the loop by using the true object. I just need to make sure if I do so, I include some sort of if branch that will break out of the loop to prevent a permanent loop. Ok, seems simple enough.

I was under the impression that I had to give the while a conditional/comparison operator so that it could function properly.

===

Thank you for the words of encouragement.

===

And my last question, for now :D, has to with with defining.

I think it’s around Chapter 9 Chris takes us into defining methods…

I have previous programming experience with a language called vbscript form the 90s, very very little I might add. I used it to help with administering several hundred computers for a school district and my scripts were very basic as I never needed anything more complex, anyway…

In that language, I remember something like this but they called it defining a function rather than a defining a method. They then stated that a method can be applied to a function as a method was just a construct that was built into the language, or something along those lines, forgive me it’s been a long time.

One of Chris’ examples looks like this…

def say_moo number_of_moos
puts 'mooooooo...'*number_of_moos
end

How am I suppose to read that?

Is it…

define, say_moo & number_of_moos as
puts ‘mooooo…’*number _of_moos
end of definition

So both say moo and number of moos are doing the same thing?

Or is say_moo the method being defined and anything after that is just a parameter variable used by the say_moo method?

===

I guess I lied, sorry I just remembered a ton of questions…

So variables have scope. As in where they can be accessed by my program.

Global means anything within the main object can access said variable. Correct? If I understand this correctly, I want to try to limit my use of global variables as much as possible due to security concerns that could come up if say I was programming a website application with Ruby on Rails or some other such framework.

Am I understanding that correctly so far?

And that I should try, whenever possible, to use local variables within a defined function/method.

===

And just to be clear…

So there is Ruby Super Global Variables built into the language.
Global Variables with the main object.
and local variables tied to the methods/functions I create which execute self contained blocks of code that return whatever values I am trying to get returned.

Am I correct or is Ruby different and I am confusing it with other programming languages?

===

Chris talks about return values in Chapter 9.

return_val = puts 'This puts returned:'
puts return_val

I didn’t understand what he was trying to say with this.

He assigns a variable to do puts and print a string.
Then he prints said string.

Before that he says…

“Notice that 5+3 returns
8; it does not output 8 (that is, display 8 on your screen)”

Is he saying that while 5+3 is evaluated by the Ruby interpreter, if I don’t explicitly tell it to puts the return value on screen since I didn’t attach it to puts, it returns 8 to the interpreter but doesn’t output the answer to the screen?

Thanks again AstonJ. Really appreciate the time you are taking to explain all this.

1 Like

You’re welcome :+1:

Functions vs methods

Generally functional languages have Functions and OOP languages have Methods - they basically do the same thing (they do something when called) and the major difference is in OOP languages you are usually calling a Method on an object.

This code:

def say_moo number_of_moos
  puts 'mooooooo...'*number_of_moos
end

is essentially the same as:

def say_moo(number_of_moos)
  puts 'mooooooo...'*number_of_moos
end

This is because parentheses are optional in Ruby :smiley: tho usually people will use them when defining a function. So, as you guessed, it means number_of_moos is a parameter that that method expects you to provide when you call it.

That sounds about right, however I wouldn’t worry about scope for now because this is something that makes more sense when you start to learn about Classes.

Return values are pretty much just that - whatever is returned after running a command. When you run puts 'This puts returned:' it’s prints “This puts returned:'” and then returns the value nil. So nil is the return value. So 5+3 will return 8.

Generally when you start creating your own methods, whatever is the output of the last command will be returned.

So for example:

def do_some_stuff
  5+5
  10+10
  1+1  
end

puts do_some_stuff

Will display 2 as that is the last return value in the method even tho it actually carried out all the other calculations too. Again, try not to worry too much about this as it will start to make sense as you progress.

Hope that helps! :smiley:

1 Like

I am confused by this?

Isn’t it returning a string, which isn’t nil since it’s not empty?

Or all strings are going to return nil?

Thanks again AstonJ.

1 Like

You can use Kernel#p if you want the return value to be the string :smiley: (so p "hello").

However generally you use puts to simply print something to standard IO (so it’s function is just that - to display that string on your screen) and is used for things like debugging or notices. So you might have a script that’s something like this:

puts "Starting script..."

if condition == true
  puts "Doing something..."
  # Do something
else
  puts "Doing something else..."
  # Do something else
end 

puts "Finished doing stuff"

If you wanted to put together a screen to be later used with puts you would just work with the string first, then use puts, or, use string interpolation. Eg:

first_name = "James"
surname = "Bond"
full_name = first_name + " " + surname
puts full_name

or with interpolation:

first_name = "James"
surname = "Bond"
puts "#{first_name} #{surname}"

Hope that helps :smiley:

Still very confused.

So was I correct in saying strings return nil, unless you specifically have the interpreter return a value for that specific string?

So while the string might print something to I/O and get to the screen, in reality the interpreter still sees it as nil? Unlike doing some math which has to be evaluated and returns a very specific value?

1 Like

A string is a value (technically, an object - as pretty much everything in Ruby is an object) and will always return itself - you can try this in an IRB shell:

irb(main):038:0> "hello"
=> "hello"
irb(main):039:0> 5
=> 5
irb(main):040:0> 10
=> 10
irb(main):041:0> 10.2
=> 10.2
irb(main):042:0> true
=> true
irb(main):043:0> false
=> false
irb(main):044:0> nil
=> nil

irb(main):045:0> "hello".class
=> String
irb(main):046:0> 5.class
=> Integer

puts is a method. Methods always return something - usually whatever you want them to return if you wrote the method yourself or whatever the author of the method specifies.

Try not getting bogged down with anything other that what Chris is specifically covering in the book for now. I usually recommend that book to anyone who tells me they’re not sure whether they can be a programmer, and I tell them to read that book and by the end of it they will know. After which, if they’re want to learn more, I would recommend other books to get into the nitty gritty (for instance, as per my guide here). My thoughts are generally > start with a beginner’s book > read another that’s a little more advanced > If -you- feel you’re ready to start on a project, go for it! If not, read another book that’s a level up. Repeat as necessary.

I’m not sure if the scope for the LTP book has changed (I read the original version a long time ago) but I would use it as a springboard for more advanced books, and simply use that book just to grok what Chris specifically covers in it :smiley:

1 Like

Yes, my book is geared for the beginner, and was specifically intended to be a springboard for the Pickaxe, which was already an excellent book. I was just trying to fill that gap.

And about my omission of parens in method definitions: that was the style 17 years ago when I first started working on this book. I am currently working on the 3rd Edition (I think this sentence is the first time I’ve said so publicly), and it includes parens for method definitions and most method calls (as well as numerous other changes, a totally new chapter, etc).

3 Likes

Can’t wait. :smiley:

1 Like

Thank you again AstonJ.

1 Like

So excited to have your new edition coming out, Chris! And thank you for being so generous with your time. I have seen how much you help your readers when they get stuck. It’s so valuable to have that kind of support when learning!

2 Likes