33b53c3fda38b2464e11ebcddefa6235

I've been working on this program for a few days now. I'm fairly new to Ruby (a little under a week into learning it) & have been following Chris Pine's "Learn to Program" tutorial, which covers various fundamental aspects of Ruby. The following was the exercise the program was written for:

• Write a Deaf Grandma program. Whatever you say to grandma (whatever you type in), she should respond with "Huh?! Speak up, sonny!", unless you shout it (type in all capitals). If you shout, she can hear you (or at least she thinks so) and yells back, "No, not since 1938! To make your program really believable, have grandma shout a different year each time; maybe any year at random between 1930 and 1950. (This part is optional, and would be much easier if you read the section on Ruby's random number generator at the end of the methods chapter.) You can't stop talking to grandma until you shout "BYE".

• Extend your Deaf Grandma program: What if grandma doesn't want you to leave? When you shout "BYE", she could pretend not to hear you. Change your previous program so that you have to shout "BYE" three times in a row. Make sure to test your program: if you shout BYE three times, but not in a row, you should still be talking to grandma.

Now, I've satisfied every requirement the exercise ascertained. But, I've been told that the indentation is funky & that the print statements could be shortened. Most significant is the fact that I seem to have "one loop too many," but I'm unsure of where it is & how to fix it. If anyone can refactor this one for me, & give me a brief explanation as to what you did, I'd appreciate it. Thanks in advance.

—houston b-g

ruby

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def granny_says(str)        
  str.each_byte do |byte|   
    print byte.chr           
    sleep 0.01
    STDOUT.flush             
  end
  puts                       
  STDOUT.flush
end

loop do
  granny_says("<Intermediary> Got a question for Granny? Make sure to"      )
  granny_says("               input yo' question in all caps, or else"      )
  granny_says("               granny       can't       hear      ya'!\n"    )
  reply = gets.chomp
    if (reply == reply.upcase)
    random = rand(21) + 1930
    granny_says("\n<Granny>  No, not since #{random}! ... that's all the time" )
    granny_says("          I've got, child!  Hasta  la-bye-bye,  baby!\n\n"    )
    granny_says("<Intermediary> Granny's a bit hard of hearing today. I"       )
    granny_says("               apologize.  Ya' may have to say \"bye\" a"     )
    granny_says("               few times for  her  to understand y'are"       )
    granny_says("               leaving, since it's rude to leave with-"       )
    granny_says("               out biddin'  yo' adieus.   3 times just"       )
    granny_says("               might   do   it!   In   a   row,  even!\n\n"   )
      
goodbyes = 0

while goodbyes != 3
you_say = gets.chomp
  if (you_say == "BYE")
    then goodbyes = goodbyes + 1
    else
      goodbyes = 0
    end
  if (goodbyes == 1)
    granny_says("\n<Intermediary> That's once!\n\n"                   )
  elsif (goodbyes == 2)
    granny_says("\n<Intermediary> That's twice!\n\n"                  )
  elsif (goodbyes == 3)
    granny_says("\n<Intermediary> That's thrice! That should do it!\n")
  else 
    granny_says("\n<Intermediary> That's either  not in caps,  not an adieu")
    granny_says("               or neither. How 'bout 'ya try that again?\n")
  end
end

        granny_says("\n<Granny> I'm not dense, youngin'! I heard you the"      )
        granny_says("         first time!  I... CAN... HEAR... JUST..."        )
        granny_says("         FINE. The nerve of you, child!  I oughta"        )
        granny_says("         bust      your          little      ass!\n\n"    )
        granny_says("<Intermediary> Ya' sure did do a fine job of pissin'"     )
        granny_says("               off yo' granny,  didn't  'ya?  If 'ya"     )
        granny_says("               wanna talk with her again,   schedule"     )
        granny_says("               an appointment  with  me  later on in"     )
        granny_says("               in the week. Give  her  some  time to"     )
        granny_says("               cool   off.   Talk    to   'ya  then!"     )
    break
  else
    granny_says("\n<Intermediary> Granny can't hear 'ya! Wanna know why?"   )
    granny_says("               'Cause that wasn't  in all caps,  yo'!"     )
    granny_says("               So, I'll repeat what I said before ...\n\n" )
  end
end

Refactorings

No refactoring yet !

1e8f141e7857d397d8020ed3b759e88a

Maciej Piechotka

July 29, 2008, July 29, 2008 20:28, permalink

No rating. Login to rate!

Only what within the task. You can change puts into whatever you want.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
count_bye = 0
loop do
  word = gets.chomp

  if word == "BYE" 
    count_bye += 1
    if count_bye == 3
      puts "Bye son."
      break
    end
  else
    count_bye = 0
  end

  if word != "BYE" and word.upcase == word
    puts "No. Not since #{1930 + rand(21)}."
  else
    puts "I cannot hear you son!"
  end
end
861f311cc4a077c439099d0e5d251e73

Wolfbyte

July 30, 2008, July 30, 2008 01:40, permalink

No rating. Login to rate!

Hey, welcome to ruby. I'm not sure where the tutorial puts you in the grand scheme of things but the below is a re-imagining of the original problem. It's less conversant that your one but the structure is nice and easy to read.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def granny_can_hear( message )
  message.upcase == message
end

def ask_user_to_speak_up
  puts "Huh?! Speak up, sonny!"
end

consecutive_goodbyes = 0
while consecutive_goodbyes < 3 do
  print "> " 
  what_you_said = gets.chomp
  if what_you_said == "BYE"
    consecutive_goodbyes += 1
    if consecutive_goodbyes < 3
      ask_user_to_speak_up
    end
  else
      consecutive_goodbyes = 0
      if granny_can_hear what_you_said
        puts "No, not since #{rand(21) + 1930}!"
      else
        ask_user_to_speak_up
      end
  end
end
puts "See you later sonny"
861f311cc4a077c439099d0e5d251e73

Wolfbyte

July 30, 2008, July 30, 2008 01:41, permalink

No rating. Login to rate!

Hey, welcome to ruby. I'm not sure where the tutorial puts you in the grand scheme of things but the below is a re-imagining of the original problem. It's less conversant that your one but the structure is nice and easy to read.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def granny_can_hear( message )
  message.upcase == message
end

def ask_user_to_speak_up
  puts "Huh?! Speak up, sonny!"
end

consecutive_goodbyes = 0
while consecutive_goodbyes < 3 do
  print "> " 
  what_you_said = gets.chomp
  if what_you_said == "BYE"
    consecutive_goodbyes += 1
    if consecutive_goodbyes < 3
      ask_user_to_speak_up
    end
  else
      consecutive_goodbyes = 0
      if granny_can_hear what_you_said
        puts "No, not since #{rand(21) + 1930}!"
      else
        ask_user_to_speak_up
      end
  end
end
puts "See you later sonny"
33b53c3fda38b2464e11ebcddefa6235

houston b-g

July 30, 2008, July 30, 2008 18:37, permalink

No rating. Login to rate!

@Wolfbyte — That's a whole lot cleaner & easier to read than the original. Only thing is, it's void of the one thing that would set it apart from the next person reading the tutorial & doing the exercise -- expanded dialect. Although, perhaps I should be more worried about /learning/ the fundamentals, rather than spending all my energy making the program "believable", per se.

However, as is, the lack of conversation leaves you wondering what exactly you should be doing. Especially come time to say "BYE" three times. Of course, it's just an example & you could've refactored it to serve as a jumping off point for a complete re-vamp of the entire program.

Regardless, I guess I don't give the people who would sit down & run the program much credit -- I don't think that many people would know to type in all caps when Granny says, "Huh!? Speak up, sonny!" Therefore, I implemented the Intermediary to explain how Granny works. Maybe I put too much thought into it & should've taken the exercise from a different, less expansive angle, but I didn't.

Anyways, thank you! I'll look over the code & implement what I can into the re-vamp of the original. It's appreciated.

@Maciej Piechotka — Thank you for the refactor. Like Wolfbyte's, it's a lot more cleaner & readable than the original, but still lacks a lot of the stuff I spoke about in my spiel to Wolfbyte. I appreciate it, though.

1e8f141e7857d397d8020ed3b759e88a

Maciej Piechotka

July 30, 2008, July 30, 2008 18:55, permalink

No rating. Login to rate!

@Wolfbyte - to keep the ruby convention: 'granny_can_hear' shouldn't be named 'granny_hear?'?

861f311cc4a077c439099d0e5d251e73

Wolfbyte

July 31, 2008, July 31, 2008 12:55, permalink

No rating. Login to rate!

@houston b-g - You are correct that our versions are a little terse but I think we probably had different target audiences in mind. Whereas you were coding a solution for an end-user, Maciej Piechotka and I were targeting our response at you (someone who was reading the code). My original solution (which I didn't post) had all of granny's dialogue (and that of the intermediary) separated out into different methods so that you'd retain the whole program with the essence of the structure intact. For the sake of not hiding the forrest (the structure) behind the trees (the prose) a lot of it got excised out. The ask_user_to_speak_up message is all that remains.

I've often found the best thing when trying to deal with a complicated section of program logic is to remove as much of the program from consideration as you can until you have just enough to remind your brain how everything fits together. If appropriate you can add in all of the other stuff later (but you'll usually keep it abstracted away).

33b53c3fda38b2464e11ebcddefa6235

houston b-g

July 31, 2008, July 31, 2008 20:08, permalink

No rating. Login to rate!

@Wolfbyte - You're absolutely correct about removing much of the program to have just enough so I can get my head around how everything fits together as the best way to deal with sections of complicated program logic. I've started doing this (as of mid-day yesterday) & it's helping me out considerably. I've gotta look at the forest as a whole before I work out its specific flora. :-)

861f311cc4a077c439099d0e5d251e73

Wolfbyte

August 1, 2008, August 01, 2008 03:11, permalink

No rating. Login to rate!

@Maciej Piechotka - Yep you're right. It's a hazard switching from ruby to C# and back again all the time.

I also had this version which is pretty compact but probably not one I'd use. It rolls the loop slightly so that the Speak Up message is printed at the top based on the last thing said. It does this with the binard heard variable which gets reset to false whenever you say something until granny hears you. To deal with this on the first iteration it fakes granny having heard something. I wouldn't use it in production because I think it's confusing but it does do the job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
byes, heard = 0, true
while byes < 3 do
  puts "Huh?! Speak up, sonny!" unless heard
  print "> " 
  what_you_said, heard = gets.chomp, false
  if what_you_said == "BYE"
    byes += 1
  else
    byes = 0
    heard = what_you_said.upcase == what_you_said
    puts "No, not since #{rand(21) + 1930}!" if heard
  end
end
puts "See you later sonny"
507ad9765784506b94461181f4d31d9a

Laurel

August 7, 2008, August 07, 2008 15:05, permalink

No rating. Login to rate!

Ruby has a ton of different ways to do string literals:

http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

For really long or multiline strings "here documents" can be the way to go:

1
2
3
4
5
6
7
8
def ask_user_to_speak_up
    granny_says <<EOS
<Intermediary> Granny can't hear 'ya! Wanna know why?
               'Cause that wasn't  in all caps,  yo'!
                So, I'll repeat what I said before ...

EOS
end
5470f9b1544dccb064c8ca1633220d41

Martin

October 8, 2008, October 08, 2008 01:09, permalink

No rating. Login to rate!

Hey, just started reading this book yesterday. I made the program and googled to see if anyone else had so I could compare :)
This is what I ended up with, how did I do?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
question = "starting question"
bye = 0
while bye < 3
  puts "Ask granny a question..."
  question = gets.chomp
  decade = (3 + rand(2)).to_s
  year = rand(10).to_s
  if question == question.upcase && question != "BYE" 
    puts "NO, NOT SINCE 19" + decade + year + "!"
    bye = 0
  else
    if question == "BYE"
      bye = bye + 1
    else
      bye = 0
    end
    if bye == 3
      puts "OH OK DEARY! BYE BYE X"
    else
      puts "HUH!? SPEAK UP, SONNY!"
    end
  end
end
5470f9b1544dccb064c8ca1633220d41

Martin

October 8, 2008, October 08, 2008 01:32, permalink

No rating. Login to rate!

After looking at it again after a short break I noticed some obvious improvements for my code, I dont know why I did the random number that way! and the initial question value was because in an earlier attempt I had used question != "BYE" in my while loop. Here is the shortened version...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bye = 0
while bye < 3
  year = rand(21) + 1930
  puts "Ask granny a question..."
  question = gets.chomp
  if question == question.upcase && question != "BYE" 
    puts "NO, NOT SINCE " + year.to_s + "!"
    bye = 0
  else
    if question == "BYE"
      bye = bye + 1
    else
      bye = 0
    end
    if bye == 3
      puts "OH OK DEARY! BYE BYE X"
    else
      puts "HUH!? SPEAK UP, SONNY!"
    end
  end
end

Your refactoring





Format Copy from initial code

or Cancel