# Section 8 -- Ruby introduction! # by Emily. =begin Brief order of business: Two weeks ago someone asked me about lambdas in Scheme with a variable number of arguments. The syntax is similar to regular functions with variable number of parameters: (define x (lambda (a b . c) (print c))) (x "e" "b" "c" "d" "e") returns ("c" "d" "e") Also, in Scheme if you don't have a match for a cond expression, the value is unspecified!! O.o =end # Today: # - Arrays, Hashes # - Blocks, Iterators # - Random other stuff # First of all: Ruby docs! The Ruby docs are awesome and super helpful! They will be your friend! # http://ruby-doc.org/ # Arrays/stacks, etc x = Array.new() #also written as [] x.push(5) x.push(Array.new()) x.push(7) #x.pop() #returns 7 puts x puts x.inspect y = x y.pop() #returns [] x.pop() #returns 5 -- x and y are aliased x.push(6).push(7) y = x.clone() y.pop() # returns 7 x.pop() # returns 7 also x = Array.new(3) # returns [nil, nil, nil] x = [5, 6, 7] x = Array.new(3) {|n| n + 5} #the index number is the argument passed into the block x = Array.new(3, 6) # returns [6, 6, 6] puts x[1] #6 x[1] = 666 puts x[1] #666 puts x[20] #nil puts x[-2] #666 puts x.inspect puts x[-4] #nil ==> from question in class x.delete(6) # deletes all values 6 from the array x # is now [666] #Now you write: an array of ten elements whose content is the square of each index value # ie [0, 1, 4, 9, 16, etc] #write a function to reverse the order of elements in your array # Hashes x = {} # also can be written as Hash.new() x["a"] = "Found A" x[false] = "Found false" puts x["a"] puts x[false] puts x.keys puts x.values #other way to instantiate hashes: x = {"emily"=>1, "cody"=>2, "hal"=>3} #also, symbols are cheaper than creating strings because they don't have to be garbaged collected later: x = {:oranges=>1, :potatoes=>2, :tomatoes=>3} x = Hash.new("NOT FOUND") #default value for the hash if not found x["a"] = "Found A" x["b"] = "Found B too" puts x["a"] puts x["c"] x.delete("a") # returns "Found A" and deletes it from the hash # Special note: these "default values" are passed by reference, not by value, so for example: x = Hash.new([]) puts x["2"].inspect x["2"].push(3) puts x[:pants].inspect #sneaky! beware! #slightly weird: h = {:radon=>3, :xenon=>4, :helium=>1} #first the key is the argument x, and then its corresponding value, and so on h.each{|x| puts x} #takes the key and value. h.each{|k,v| puts k} #wordcount is so short in Ruby! #words = File.open('filename.txt') {|f| f.read }.split #freqs=Hash.new(0) #words.each { |word| freqs[word] += 1 } # ' ' = string literal # " " = may contain things that can be evaluated # Class variables and methods class Foo @@counter = 0 # static field. def initialize(bananas) @@counter += 1 # (Note to you Java/C programmers: ++ does not exist in ruby.) @@counter += bananas puts @@counter end def Foo.report1 @@counter x = 3 + 1 end def self.report2 # same thing as Foo.report1 @@counter end attr_reader :readable_field #the instance variable @accessible_field can be read using the method attr_writer :writeable_field #the instance variable itself has private visability attr_accessor :rw_field end x = Foo.new(40) y = Foo.new(25) numFoos = Foo.report1 # returns 2 -- note it doesn't require a Foo object to run z = Foo.new(50) puts z.readable_field # standard convention: usually functions don't do mutation (returns the new value). # A function that does mutations uses the ! (like Scheme) to show the value's going to be changed. # Other conventions: variables that start with with capital letters = constants # a wee bit more on procs, blocks, and lambdas: # blocks: just a section of code you want to do something with, say: x = [1,2,3] x.each {|i| puts i} #now, say you want to pass a block to a function: def pickles(otherValue) puts :are_tasty! puts otherValue x = [1,2,3] yield(x) #pass in the parameter to the block (empty parens if no parameters to pass in). You'll get an error if you don't pass in a block and this function expects one. puts 'all done!' yield(x) end pickles(3) {|i| puts "processing"} #but, what if I want to pass in multiple blocks and name them? # enter, Procs. #Useful if you want to use a chunk of code multiple times. printStuff = Proc.new {|i| puts i} printStuff.call('e') def hotdogs(proc_code, ketchup, onions) proc_code.call(3) puts ketchup end hotdogs(printStuff, 3, 4) #lastly, lambdas: def foo f = Proc.new { return "return from foo from inside proc" } f.call # control leaves foo here return "return from foo" end def bar f = lambda { return "return from lambda" } f.call # control does not leave bar here return "return from bar" end puts foo # prints "return from foo from inside proc" puts bar # prints "return from bar" ################################### #map and collect (are aliased to one another) puts ((1..4).collect {|i| i*i }).inspect #=> [1, 4, 9, 16] puts ((1..4).collect { "cat" }).inspect #=> ["cat", "cat", "cat", "cat"] #reduce and inject = fold in ruby # Same using a reduce and inject puts (5..10).inject {|sum, n| sum + n } #=> 45 #If you do not explicitly specify an initial value for memo, then uses the first element of collection is used as the initial value of memo #Also, puts (5..10).reduce(0) {|sum, n| sum + n } #=> 45, 0 is the initial value. # Sum some numbers puts (5..10).reduce(:+) #=> 45 (passed to the method named "+") # find the longest word longest = ["cat", "sheep", "bear"].inject { |cur_max,word| # do | | ... end is alternative syntax for blocks {|| ... } cur_max.length > word.length ? cur_max : word } puts longest #=> "sheep" # Sadly: ruby does not optimize tail recursion. :-(