CS312 - Spring 2012 - Class 3
administrative
- assignment 1?
- assignment 2 out today
- should be easier than the last one
- start early, though!
- use the examples from class and the notes from class
- my view on homework...
variable
- what is a variable in Ruby?
- a variable is a reference to an object
- this is almost the same as Java (minus built-in types)
- do variables have types?
- No! Unlike in Java, a variable can reference any type of object
>> x = 10
=> 10
>> x
=> 10
>> x = "this is a string"
=> "this is a string"
>> x
=> "this is a string"
- When I type the following, what would the memory picture look like?
>> x = "this is a string"
=> "this is a string"
>> y = x
=> "this is a string"
- What would be the value of y after the following?
>> x.reverse!
=> "gnirts a si siht"
>> x
=> "gnirts a si siht"
- as an aside, notice that Strings are mutable in Ruby, unlike many other languages
- y would also have "gnirts a si siht", since it references the same object
arrays
- what is an array?
- Ruby has arrays
- they are a class of objects like everything else in Ruby
- you can create them
- using []
>> a = []
=> []
>> b = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
- or using new
>> c = Array.new
=> []
- arrays are indexable by integers with indices start at 0
>> b[0]
=> 1
>> b[1]
=> 2
- and we can do all the fancy indexing tricks that we saw with strings
>> b[-1]
=> 5
>> b[1..3]
=> [2, 3, 4]
>> b[1...3]
=> [2, 3]
- .. and ... are called Ranges
- .. is inclusive
- ... includes the first number but not the last number
- arrays are infinitely (well almost) expandable
>> b
=> [1, 2, 3, 4, 5]
>> b << 6
=> [1, 2, 3, 4, 5, 6]
>> b << 7
=> [1, 2, 3, 4, 5, 6, 7]
>> b[10] = 15
=> 15
>> b
=> [1, 2, 3, 4, 5, 6, 7, nil, nil, nil, 15]
- you never get an array out of bounds exception, but you will get nil if you access an entry that doesn't exist
>> b[100000000]
=> nil
- you can do some fancy things with assigning to multiple entries
>> => [1, 2, 3, 4, 5, 6, 7, nil, nil, nil, 15]
>> b[1,3] = 20
=> 20
>> b
=> [1, 20, 5, 6, 7, nil, nil, nil, 15]
>> b[5,3] = 30
=> 30
>> b
=> [1, 20, 5, 6, 7, 30, 15]
- Read the book for more details on this!
- arrays have a whole bunch of useful methods that are worth browsing through
-
http://ruby-doc.org/core-1.9.3/Array.html
- count
- replace
- reverse
- shuffle
- sort
- ...
hashes (aka dictionaries or maps or associative arrays)
- how are hashes/dictionaries/maps different than arrays?
- arrays can only have positive integers as indices/keys
- arrays are sequential
- a hash is an association between a key value pair
- they're called hashes since they're often implemented with hashtables (but they don't have to be)
- keys can be any object
- values can be any object
- in most situations the lookup of a key is a constant time operation
- We can construct new hashes
- using {}
>> a = {}
=> {}
>> b = {"key1" => "value1", "key2" => "value2"}
=> {"key1"=>"value1", "key2"=>"value2"}
- or using new
>> c = Hash.new
=> {}
- hashes are indexed by arbitrary keys
>> b["key1"]
=> "value1"
>> b["banana"]
=> nil
>> b["banana"] = 1
=> 1
>> b
=> {"key1"=>"value1", "key2"=>"value2", "banana"=>1}
- hashes are very versatile and are very frequently used
blocks
- how would you print out the values in an array e.g. b = [1, 2, 3, 4, 5]?
1.
for val in b
puts val
end
2.
for i in 0...b.length
puts b[i]
end
3.
i = 0
while i < b.length
puts b[i]
i += 1
end
- all of these work fine in Ruby, however, it is generally not the preferred style or the approach the people take
- in Ruby, the common approach is to use "blocks"
1.
b.each{ |val| puts val}
2.
b.each do |val|
puts val
end
- a block is similar to an anonymous function and has multiple parts
- the parameters are listed in between ||
- the blocks in the example above just have two parameters
- the body of the block is listed after it
- block are either denoted with {} or with do... end
- the two above are equivalent
- in practice, one line blocks are usually written with {} and multiline with do...end
- functions and methods utilize the blocks internally (think of it like passing a block of code for the function to use)
- in the example above, "each" is an iterator and applies the block of code to each values in the array
- this has some very powerful implications
- sum up the values
sum = 0
b.each{|va| sum += val}
- sum up the square of the values
sum = 0
b.each{|v| sum += v * v }
- sum up the values and print the rolling sum
sum = 0
b.each do |v|
sum += v
puts v
end
- what if we wanted to print out the rolling sum and the index?
sum = 0
b.each_with_index do |value, index|
sum += v
puts "#{index}:\t#{value}"
end
- notice that the function/method that you call will dictate how many parameters the block needs to have, but you dictate the names
- any code that you want to execute can go in the body of a block
- other functions also use blocks
- select
>> b = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> b.select{|val| val < 3}
=> [1, 2]
>> (0..100).select{|v| v %2 == 0}
=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
- each_char
- like each, but for strings
"this is a test".each_char{|ch| puts ch}
writing blocks
- the keyword "yield" specifies a call to a block that was passed to a function
def array_checker(a)
vals = []
a.each{|v| vals << yield(v)}
return vals
end
p array_checker([1, 2, 3, 4, 5, 6]){|v| v >= 3}
- this is basically the map function
p [1, 2, 3, 4, 5, 6].map{|v| v >= 3}
- anywhere the the keyword "yield" is used, the block is called
- the number of parameters used with the yield command dictates how many parameters the block must have
def some_function(num)
puts yield(num, num)
puts yield(num, 1)
end
some_function(10){|v1, v2| v1 * v2}
some_function(10){|v1, v2| v1 + v2}
blocks and hashes
- hashes also have methods that use blocks
- the key difference is that hashes have keys and values
- print out the keys and values
h.each{|key, value| puts "#{key}\t#{value}"}
sorting
- may classes have a sort_by method that takes a block of code
- the block dictates a transformation on each value before being sorted
- allows for many interesting sorting techniques
- arrays
- sort normally
[-5, 4, -3, 2, 1].sort_by{|v| v}
- if you just want to do this, there is a normal sort function
- sort in reverse order
[-5, 4, -3, 2, 1].sort_by{|v| -v}
- or could also call .reverse on what was returned above
- sort based on the absolute value
[-5, 4, -3, 2, 1].sort_by{|v| v.abs}
- hashes
- hashes have both keys and values and the sort_by block can access either of these
- the sort_by function returns an array of arrays (with each sub-array containing to entries)
- sort by keys
h.sort_by{|key, value| key}
- notice that the block has both key and value as a parameter
- sort by values
h.sort_by{|k,v| v}
- sort by values decending
h.sort_by{|k,v| v}.reverse
- print out the keys sorted by value
h.sort_by{|k,v| v}.each{|k, v| puts k}
take a look at the
LinkedListImproved code
- we can define these methods in our own classes to make life easier
- each method
- traverse the linked list
- call yield (the block passed in) on each value
- each_with_index
- traverse the linked list
- keep track of the index internally
- the block (i.e. yield) expects a second parameter
- find
- returns the value of the first value where yield called on the value returns true
- returns nil if no matches exist
- can use our each method already