04 Lesson 2: Containers, Blocks, and Iterators; Standard Types; More About Methods

= Lesson 2: Containers, Blocks, and Iterators; Standard Types; More
About Methods

== Reading

If you're using the paper or pdf 2nd edition, the reading is chapters
4-6, pages 40 to 80.

If you're using the online 1st edition, the reading is:

http://www.rubycentral.com/book/tut_containers.html
http://www.rubycentral.com/book/tut_stdtypes.html
http://www.rubycentral.com/book/tut_methods.html

With the last lesson, Akkana was wondering when yield and blocks were
useful, and Anne had asked about how objects could send messages to
other objects. These readings will hopefully provide some more
information about these topics.

Here are two more pages that might be useful:

Ruby idioms: http://www.rubygarden.org/ruby?RubyIdioms
Provides a few elegant ways to do common things in Ruby.

Annotated Ruby docs: http://ruby.outertrack.com/
Read and leave comments and annotations on the standard documentation.

== Homework

=== Questions

0. Same as last time, is there anything you didn't understand? would
like to learn more about?

1. How many different ways can you think of to write the
following string declaration code?
----
name = "Laurel"
----

There are plenty of ways to answer this, so be creative :)

2. What are the 4 things you can specify when calling a method? In
what situations are each of them optional, not allowed, or
mandatory?

3. What does this do?

(first, second, third) = "1 2 3".split(/ /)

p first + third

=== Problems

1. Blocks and iterators:

Rewrite the findLongWords method below using iterators and blocks
instead of the for loop:

----
# Given an array of words, findLongWords returns a new array
# containing the words with more than 5 letters.
def findLongWords(words)
longWords = Array.new()
for i in 0...words.length
if !words[i].nil? && words[i].length > 5 # long words have more
than 5 letters
longWords << words[i]
end
end
longWords
end

p findLongWords(['thisOneIsLong', 'short', 'howAboutThisOne' ])
----

Hint: look at the documentation for the Enumerable module:
http://www.ruby-doc.org/core/classes/Enumerable.html

2. Blocks and sort

Normally, if you call sort on an array of strings, it'll sort by their
lexical value (ie. alphabetically). Write some code that will sort an
array of strings first by number of vowels (fewest first), and then by
lexical value (for strings of the same length. For the sake of this
problem, let's say that a, e, i, o, and u are the only vowels.

For example:

Normal sort would do:

['dog', 'cat', 'aardvark', 'bird'].sort
#=> ['aardvark', 'bird', 'cat', 'dog']

Your code should return:

['bird', 'cat', 'dog', 'aardvark']

You can use the Enumerable.sort function:
http://www.ruby-doc.org/core/classes/Enumerable.html#M001944

Bonus: use Enumerable.sort_by:
http://www.ruby-doc.org/core/classes/Enumerable.html#M001945
(the example at the link above)

3. Implement some methods

Below is a skeleton class definition for the /proc information class
we talked about in the last lesson. Implement the blank methods.

----
class ProcessInfo
# initialize a ProcessInfo object with:
# pid process id
# cmdline complete command line of the process
# cwd name of the working directory of the process
# exe filename of the executable program
# files array of files that the process has open
def initialize(pid, cmdline, cwd, exe, files)
# Please implement
end

# calls the given block once for each open file, passing the file
# name as a parameter.
def each_open_file
# Please implement
end

# add the provided file names to the process' list of open files
def add_open_files(*new_files)
# Please implement
end

# return a string representation of the process information
def to_s
# Please implement
end
end


### test code

test_process = ProcessInfo.new(20,
"emacs -nw my_program.rb my_library.rb",
"/home/laurel",
"/usr/bin/emacs",
["/home/laurel/my_program.rb",
"/home/laurel/my_library.rb"]);

print "Before adding files:\n"
print test_process.to_s
print "Process #{test_process.pid} has the following files open:\n"
test_process.each_open_file do |open_file|
print " - #{open_file}\n"
end

test_process.add_open_files("/home/laurel/another.rb", "/home/laurel/more.rb")

print "\nAfter adding files:\n"
print test_process.to_s
print "Process #{test_process.pid} has the following files open:\n"
test_process.each_open_file do |open_file|
print " - #{open_file}\n"
end

----

The output of the test code should look something like:

----
Before adding files:
20: emacs -nw my_program.rb my_library.rb
cwd=/home/laurel, exe=/usr/bin/emacs, 2 open files
Process 20 has the following files open:
- /home/laurel/my_program.rb
- /home/laurel/my_library.rb

After adding files:
20: emacs -nw my_program.rb my_library.rb
cwd=/home/laurel, exe=/usr/bin/emacs, 4 open files
Process 20 has the following files open:
- /home/laurel/my_program.rb
- /home/laurel/my_library.rb
- /home/laurel/another.rb
- /home/laurel/more.rb
----

Bonus: I don't think the constructor is very nice to use. I have to
remember all of the arguments and what order they're in, so it's easy
to make a mistake and end up with the cmdline as the cwd, etc. What
are some other ways you could write it?

--
Laurel Fan
http://dreadnought.gorgorg.org