vonronne@truffle:~/private/15110/practice$ irb Basic IRB Expressions: >> 2+3 => 5 >> x = 1.5 => 1.5 >> 2 * x => 3.0 >> 2.0 + 3 => 5.0 >> 5.0 /2 => 2.5 >> 5 / 2 => 2 Using Constants (PI and E) in the Math module: >> Math::PI => 3.14159265358979 >> Math::E => 2.71828182845905 >> 2 * Math::PI => 6.28318530717959 Using Functions (sqrt and sin) in the Math module: >> Math.sin(0.5*Math::PI) => 1.0 >> Math.sin(Math::PI) => 1.22464679914735e-16 >> Math.sqrt(2) => 1.4142135623731 >> Math.sqrt(9) => 3.0 Defining a Function: >> def add_three(n) >> return 3 + n >> end => nil >> add_three(5) => 8 >> add_three(5.2) => 8.2 For Loops: >> for i in 1..10 do ?> print "step\n" >> end step step step step step step step step step step => 1..10 >> for i in 1..10 do ?> print i >> print "\n" >> end 1 2 3 4 5 6 7 8 9 10 => 1..10 >> def print_even() >> for i in 1..5 do ?> print 2*i >> print "\n" >> end >> end => nil >> print_even() 2 4 6 8 10 => 1..5 Here we used gedit to create a file called "braking_distance.rb" containing the following definition: def braking_distance(init_speed, deceleration) return (init_speed ** 2.0) / (2.0 * deceleration) end As long as, the init_speed and deceleration are in complementary units (e.g., speeds in meters/second and deceleration in meters/second/second), this function calculates the distance it would take for a vehicle to stop when applying a constant deceleration from an initial speed. >> load "braking-distance.rb" => true >> braking_distance(22,7) => 34.5714285714286 (22 m/s is a little less than 50mph; 7 m/s^2 is 23 fps) Then we used gedit to modify braking_distance, so that it will accept speed in miles per hour and deceleration in feet per second. def braking_distance_english(init_speed_in_mph, deceleration_in_fpsps) init_speed_in_fps = 5280.0 / 3600 * init_speed_in_mph return (init_speed_in_fps ** 2.0) / (2.0 * deceleration_in_fpsps) end Note: irb does not know anything about miles/hour or feet/second, to ruby, "init_speed_in_mph," "deceleration_in_fpsps", and "init_speed_in_fps" are just three distinct, but arbitrary, variable names. As far as Ruby is concerned it could just as easily have been defined: def braking_distance(x, y) z = 5280.0 / 3600 * x return 0.5 * ((z ** 2.0) / y) end >> load "braking-distance.rb" => true >> braking_distance(20,22.5) => 19.120987654321 >> braking_distance(30,22.5) => 43.0222222222222 >> braking_distance(40,22.5) => 76.4839506172839 >> braking_distance(20,15) => 28.6814814814815 >> braking_distance(20,30) => 14.3407407407407 Now we can use a loop to print out a table of stopping distance for different speeds: def braking_table() print "Breaking Distance in Meters (at 7m/s/s deceleration)\n\n" print "speed\tdistance\n" for i in 1..5 do speed = 10+5*i d = 0.5 * speed**2 / 7 print speed print "\t" print d print "\n" end return nil end >> load "braking_table.rb" => true >> braking_table() Breaking Distance in Meters (at 7m/s/s deceleration) speed distance 15 16.0714285714286 20 28.5714285714286 25 44.6428571428571 30 64.2857142857143 35 87.5 => nil We can also modify the table-function to use the result calculated by the braking_distance or braking_distance_english function: def braking_table2() print "Breaking Distance at Various Speeds (at 22.5 f/s/s)\n\n" print "mph\tdistance (f)\n" for i in 1..10 do speed = 25+5*i d = braking_distance_english(speed, 22.5) print speed print "\t" print d print "\n" end return nil end >> load "braking_distance.rb" => true >> braking_table2() Breaking Distance at Various Speeds (at 22.5 f/s/s) mph distance (f) 30 43.0222222222222 35 58.558024691358 40 76.4839506172839 45 96.8 50 119.506172839506 55 144.602469135802 60 172.088888888889 65 201.965432098765 70 234.232098765432 75 268.888888888889 => nil In Ruby, "=" is used for assignment. When an assignment operation is executed, it calcuates the right side of the "=" using the current values, so: >> x = 5 => 5 >> x = x + 1 => 6 >> print x, "\n" 6 => nil >> x = x + 1 => 7 >> x = x + 1 => 8 >> x = x + 1 => 9 >> ?> for i in 1..10 do ?> x = x + 1 >> end => 1..10 >> x => 19 Using gedit we can create a file summation.rb that contained the following function that sums up the numbers, 1, 2, 3, 4, 5. def summation() sum = 0 for i in 1..5 do sum = sum + i end return sum end >> load "summation.rb" => true >> summation() => 15 So we can see what is going on, we can use gedit to modify the summation definition to print out a line containin the current i, the old sum, and the new sum, each iteration of the loop. def summation() sum = 0 for i in 1..5 do print i, "\t", sum sum = sum + i print "\t", sum, "\n" end return sum end >> load "summation.rb" => true >> summation() 1 0 1 2 1 3 3 3 6 4 6 10 5 10 15 => 15 We can also generalize our definition of summation, so that it sums up the integers less than or equal to n for any n. def summation(n) sum = 0 for i in 1..n do sum = sum + i end return sum end >> load "summation.rb" => true >> summation(100) => 5050 As another example, consider a passage from p. 9 in Blown to Bits It describes the exponential growth of a disease in a population of 10,000,000 people. In this scenario, in each day, each person who was newly infected in the previous day infects two new people (the basic reproduction number is 2). "In a week, 128 people come down with the disease in a single day, and twice that numbe are now sick, but in a poluation of ten million, no one notices. Even after two weeks, barely three people in a thousand are sick. But after another week, 40% of the population is sick, and society collapses." Using gedit, we can create a function newly_infected in newly-infected.rb that will print a table showing the number of new people being infected on the nth day after the outbreak occurred. def newly_infected() new_infections = 1 for n in 1 .. 21 do new_infections = 2 * new_infections print n print " " print new_infections print "\n" end end >> load "newly-infected.rb" => true >> newly_infected() 1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024 11 2048 12 4096 13 8192 14 16384 15 32768 16 65536 17 131072 18 262144 19 524288 20 1048576 21 2097152 => 1..21 This code can be expanded to also print out the total number of people who have been infected on the nth day and the rate of infection in the population as a whole. def disease_spread(reproduction_number, population, days) new_infections = 1 num_sick = 1 for time in 1 .. days print time print " " new_infections = new_infections * reproduction_number print new_infections print " " num_sick = num_sick + new_infections print num_sick print " " ratio = 1.0 * num_sick / population print ratio print "\n" end end >> load "disease-spread.rb" => true >> disease_spread(2, 10000000, 21) 1 2 3 3.0e-07 2 4 7 7.0e-07 3 8 15 1.5e-06 4 16 31 3.1e-06 5 32 63 6.3e-06 6 64 127 1.27e-05 7 128 255 2.55e-05 8 256 511 5.11e-05 9 512 1023 0.0001023 10 1024 2047 0.0002047 11 2048 4095 0.0004095 12 4096 8191 0.0008191 13 8192 16383 0.0016383 14 16384 32767 0.0032767 15 32768 65535 0.0065535 16 65536 131071 0.0131071 17 131072 262143 0.0262143 18 262144 524287 0.0524287 19 524288 1048575 0.1048575 20 1048576 2097151 0.2097151 21 2097152 4194303 0.4194303 => 1..21 Note: 3.0e-07 is Ruby's version of scientific notation. It represents a floating point number (approximately) equal to 0.0000003 (three times 10 to the negative 7th power). That is 3 ten-millionths. COUNT DOWN We can create a simple stop watch by looping around the sleep operation: >> for i in 1..5 do ?> print i >> sleep 1 >> end 12345=> 1..5 But, note that going backwards doesn't work: >> for i in 5..1 do ?> print i >> sleep 1 >> end => 5..1 What we can do, however, is calculate a decreasing value in the loop based on the increasing variable i: >> for i in 1..10 do ?> print 11 - i >> sleep 1 >> print "\n" >> end 10 9 8 7 6 5 4 3 2 1 => 1..10