Skip to content

Instantly share code, notes, and snippets.

@kmandreza
Last active March 11, 2016 23:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kmandreza/6747571 to your computer and use it in GitHub Desktop.
Save kmandreza/6747571 to your computer and use it in GitHub Desktop.
Let's Play 2
Exercise 1 Deaf Grandma
We're going to model something a little silly: an interaction between you and your imaginary deaf grandma. She exhibits the following inexplicable behavior:
1.She can only hear you if you shout at her.
2.If you say something but don't shout, she'll shout right back: "HUH?! SPEAK UP, SONNY!"
3.If you do shout you're also out of luck, because she'll misunderstand you and shout back "NO, NOT SINCE 1983!"
4.She won't let you leave the room unless you say, politely, "I love ya, Grandma, but I've got to go." She may be deaf, but she can smell rude a mile away.
How should these behaviors map to code?
"Real" world Code world
Starting a conversation with Grandma Running the program via the command line
Speaking to your Grandma Reading a line of user input with the gets method.
Grandma speaking to you Printing a line to the console using the puts method.
Shouting Either entering or printing text IN ALL CAPS, depending on who is speaking.
Leaving the conversation Exiting the program
Learning Goals
Understanding the relationship between two models and how change in one is reflected by change in the other
Remembering state and working with variables
Nested loops and conditionals
As you are coding, ask yourself....
Are you writing a single, gigantic method or breaking down your program into logical units?
Objectives
Deaf Grandma Doesn't Gets It
Write a method called deaf_grandma that models the Grandma-talkin' rules above. Use gets to prompt the user for input.
Changing the Requirements
After you have a program that allows you to leave the conversation with Grandma in a civil way, we're going to add a new requirement. In addition to saying "I love ya, Grandma, but I've got to go." to end the conversation, you also need to silently move away. Set up a new way to exit the program in your method: when two empty lines are entered in succession by the user. (Both conditions for ending the program should be supported!)
How does this change your program? How do you record the "state" of your interaction with Grandma?
Getting Creative (Optional)
Consider some further changes to the rules above. Let's say we want to model some new behavior in our system. Think of how these "real world" scenarios might be modeled in "code world."
How would you model non-verbal actions, like giving your Grandma a hug?
What if Grandma's behavior changed depending on her mood? Maybe she's happy in the morning but grumpy at night.
What if Grandma wants to pinch your cheeks every time you visit?
What other scenarios can you think of?
Have some fun! Think of the craziest scenario you can and write a version of Deaf Grandma called deaf_grandma_crazy which models that scenario.
Exercise 2 Pig Latin
Here's a story every programmer knows. Your friend George comes up to you one day and asks, "I have an idea for a script, but I don't want to write it. Will you, my talented programmer friend, do it for me?"
In this situation your job will involve:
1.Understanding the picture George has in his head of what he wants built and why
2.Creating various representations of what you think he wants, ranging from product specifications, to wireframes and user stories, to pseudocode used to communicate with other engineers.
3.Implementing prototypes and iterating towards a finalized product.
As a programmer, you will be expected to build code from all kinds of specifications: user stories, wireframes, pseudocode. It's important to be able to understand how to read these models and translate them into a functional program.
More importantly, you have to understand the value each model has. Pseudocode, for example, is primarily used to communicate the essence of an algorithm without getting bogged down in language-specific syntax. A good programmer can take well-written pseudocode and translate it into functional code in the language of his choice.
Learning Goals
Modeling a real world problem
Abstracting the core details from a real world problem
Reading and Writing Psuedocode
Objectives
Build from Pseudocode
Here's some pseudo for a pig_latin program.
Script: CONVERT TO PIG LATIN
Iteration One: CONVERT SINGLE WORD
GET a word from user input
IF the word starts with a vowel, don't change it
ELSE replace the word with its pig latin equivalent
GET all of the consonants before the first vowel in the word
SET the consonants at the end of the word and add the suffix "ay"
ENDIF
PRINT the pig-latin-ified word
Iteration Two: CONVERT COMPLETE SENTENCE
GET a sentence from user input
FOR each word in the sentence
CONVERT SINGLE WORD
ENDFOR
PRINT the converted sentence
DISPLAY the number of words converted
Write code that accomplishes the expectations laid out in the pseudocode above.
Exercise 3 Roman Numerals
We're going to write a method that converts an integer to its Roman numeral equivalent, i.e., 476 => 'CDLXXVI'.
For reference, these are the building blocks for how we encode numbers with Roman numerals:
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
Roman Numerals as Representation
Have you ever seen a 5? I don't mean the symbol we write on a piece of paper or print to a screen, but an actual, honest-to-goodness 5?
Of course not. You've seen things that somehow embody five: five apples, five fingers, five weekdays on the calendar, a scrap of paper with "5" written on it, and so forth. Think of all the ways you can represent the integer 5.
Symbols like 5, "five", V, and IIIII are one way. If you asked a three-year-old, they might hold up the five fingers on their hand or pull out five pennies from their pocket. Computers encode numbers as a sequence of 0s and 1s called binary.
The map is not the territory, as they say.
Objectives
Old-school Roman numerals
In the early days of Roman numerals, the Roman's didn't bother with any of this new-fangled subtraction 'IX' nonsense. No sir, it was straight addition, biggest to littlest--so 9 was written 'VIIII' and so on.
Write a method to_roman that when passed an integer between 1 and 3000 (or so) returns a string containing the proper old-school Roman numeral.
In other words, to_roman(4) should return the string 'IIII'.
Make sure to test your method by passing it several inputs whose results you know. Test some simple numbers like to_roman(1) and more complicated numbers like to_roman(1646). This serves as a good sanity check.
Hint: Use the integer division / and modulus % methods.
Modern Roman numerals
Eventually, someone thought it would be terribly clever if putting a smaller number before a larger one meant you had to subtract the smaller one. As a result of this development, you must now suffer.
Rewrite your previous method to return the new-style Roman numerals so when someone calls to_roman(4), it should return the string 'IV'. You might want to run a script like this to make sure your program is working as intended:
puts "My totally sweet testing script"
puts ""
puts "input | expected | actual"
puts "------|----------|-------"
puts "4 | IV | #{to_roman(4)}"
puts "9 | IX | #{to_roman(9)}"
puts "13 | XIII | #{to_roman(13)}"
puts "1453 | MCDLIII | #{to_roman(1453)}"
puts "1646 | MDCXLVI | #{to_roman(1646)}"
Examples
Arabic Roman
4 IV
9 IX
14 XIV
44 XLIV
944 CMXLIV
Roman Numerals vs. Arabic Numerals: Pros and Cons
Reflect for a second on the pros and cons of each representation. Imagine you're an engineer building a system for people to manipulate numbers and you have two proposals before you: use Roman numerals or use the Arabic numerals we use today. How do you decide and why?
What benefits do Arabic numerals have over Roman numerals as a way to represent numbers? For example, with Arabic numerals we have an obvious way to represent 0. Arabic numerals also typically require fewer characters to represent the same number, e.g., "3111" vs "MMMCXI".
What else? This is a useful exercise in understanding the relationship between how you represent your data and the actions you want to perform on your data — a dynamic you'll see at play in almost every piece of software you write.
For example, if we're counting people as they walk into a room by marking something on a piece of paper, Arabic numerals are a terrible representation. That'd be like trying to go for a hike and using a political map as a guide.
Instead, we opt for using tally marks to count.
Exercise 4
We're going to have you implement two versions of the Fibonacci sequence: an iterative version and a recursive version. We'll compare the performance of each and discuss potential improvements. They'll be methods called fib_iterative and fib_recursive, respectively, which take an integer n as input and returns the nth Fibonacci number.
Each version will work as follows:
def fib_iterative(n)
# returns the nth Fibonacci number
end
fib_iterative(0) = 0
fib_iterative(1) = 1
fib_iterative(2) = 1
fib_iterative(3) = 2
fib_iterative(4) = 3
fib_iterative(5) = 5
# etc…
Although writing a method to return Fibonacci numbers might seem contrived, we work through it because the rules of the system are easy to model in code. It helps us understand what a makes a good model or a bad model, and also different ways to model the same system, e.g., even though, functionally, an iterative and recursive solution produce the same output given the same input, they perform very differently.
External Resources
Fibonacci Numbers on Wikipedia
Doodling in Math: Spirals, Fibonacci, and Being a Plant: Part 1, Part 2, and Part 3
Ruby Kickstart - Introduction to Recursion
Learning Goals
Modeling a simple real-world system in Ruby code
Method definition, arguments, and return values
Simple looping and iteration
Simple recursion
Understanding basic performance considerations, benchmarking, and tradeoffs between memory and speed
As you're coding, ask yourself…
What kind of values can my method take as its input/arguments?
What kind of values can my method return?
Are there any tradeoffs I'm making? Memory versus speed? Readability versus speed? On what side of those tradeoffs am I falling and why? What might my code look like if I fell on the other side of those tradeoffs?
Objectives
Iterative Version
Write an iterative method to calculate Fibonacci numbers called fibonacci_iterative. This means you should use looping methods like Fixnum#times, Fixnum#upto, Array#each, etc.
Recursive Version
Write a recursive method to calculate Fibonacci numbers called fibonacci_recursive. This means you should not use any loops, but instead
After you're done, ask yourself…
Are there any method arguments that don't make sense? What should my program do in that situation? "Ignore them" is one valid answer — what are others?
How large can my input be before it takes too long for my program to run? How long is "too long?"
Context and History
Where do Fibonacci numbers come from?
The Fibonacci sequence was named after Leonardo of Pisa, also known as Fibonacci, who created an idealized model of how rabbits breed. His greatly simplified model worked thus:
At the beginning of the first month we start with a pair of newborn rabbits, one male and one female
Rabbit pairs can't mate for their first month of life, but can starting at the end of their first month and do so at the end of every month thereafter
A pregnant rabbit takes one month to give birth and always gives birth to a new male/female rabbit pair
Rabbits never die
He then asked, "How many pairs of rabbits will there be after a year?" In the first month there is 1 pair. They mate at the end of their first month, but there is still only 1 pair. They give birth to a new pair at the end of their second month, at which point the original pair mates again. There are two pairs of rabbits, now.
At the end of the third month, the original pair gives birth again, so there are three pairs. The first pair and second pair can now both mate, and do, so at the end of the fourth month they give birth and there are five pairs. And so on.
In other words, at the end of each month the set of rabbits that can breed are all the rabbits that have bred before plus the rabbit pairs born at the end of the last month.
The sequence goes 1, 1, 2, 3, 5, 8, 13, etc. This sequence is called the "Fibonacci sequence" and the Nth item in this sequence — the number of rabbits alive after N months in Fibonacci's rabbit model — is called the Nth Fibonacci number.
We can restate the rules like this:
rabbits_at_month(1) = 1
rabbits_in_month(2) = 1
rabbits_in_month(n) = rabbits_in_month(n-1) + rabbits_on_month(n-2)
We've defined the rules of a system, here, and you write Ruby code which models that system. I give you some number of months, n, and your Ruby program gives me the number of rabbits alive after n months.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment