The Ruby Programming Language

Lecture Notes for CS 142
Winter 2015
John Ousterhout

  • Additional reading for this topic:
    • Read Chapter 1 of The Ruby Programming Language, then skim Chapters 2-7 to get a feel for the operators, statements, and class facilities. You can read more of the details as you are working on the projects.
    • Read Sections 8.9 and 8.10 of The Ruby Programming Language. You will need this information for Project #2.
  • Ruby basics:
    • Scripting language (more dynamic, unstructured than Java)
    • Metaprogramming facilities (write programs that write programs)
    • Pure object oriented: absolutely everything is an object
    • Large language, many features
  • Simple code example:
    sum = 0
    i = 1
    while i <= 10 do
        sum += i*i
        i = i + 1
    end
    puts "Sum of squares is #{sum}\n"
    
  • Syntactic differences from Java and C:
    • No variable declarations
    • Newline terminates statements (semicolons OK too)
    • do...end instead of {...}.
    • Optional parentheses in method invocation
  • Variables:
    • Dynamically typed: types associated with values, not variables
      • Any variable can hold any value
    • No declarations: variables created on first assignment
    • Variable scope determined syntactically:
      • foo: local variable
      • $foo: global variable
      • @foo: instance variable in an object
      • @@foo: class variable
      • MAX_USERS: constant (by convention)
  • Built-in datatypes:
    • Numbers:
      • Integer (Fixnum and Bignum)
      • Float
      • Complex
      • BigDecimal
      • Rational
    • Strings:
      • Single quotes (backslash escapes only ' and \):
        s = 'Bill\'s "personal" book'
        
      • Double quotes (lots of backslash sequences):
        s = "Found #{count} errors\nAborting job\n"
        
      • %q (like ' '), %Q (like " "):
        s1 = %q(String contains quotes: '"')
        s2 = %Q|More quotes ("") and subs: #{x}|
        
        Can include nested delimiters without escaping.
      • Here documents:
        doc = <<END
        First line
        Second line
        END
        
    • Symbols:
      • Notation: :foo
      • A string constant, more efficient in many situations
      • Typically used for unique identifiers such as variable and method names, hash keys, etc.
    • true, false, nil
    • Arrays:
      x = Array.new
      x << 10
      x[1] = 99
      y = ["Alice", 23, 7.3]
      x[2] = y[1] + y[-1]
      
      • -1 index refers to last element
    • Hashes:
      person = Hash.new
      person["last_name"] = "Rodriguez"
      person[:first_name] = "Alice"
      order = {"item" => "Corn Flakes", "weight" => 18}
      order = {:item => "Corn Flakes", :weight => 18}
      order = {item: "Corn Flakes", weight: 18}
      
    • Range: [1..20]
  • Operators and expressions: fairly similar to Java.
  • Statements:
    if x < 10 then
      ...
    elsif x < 20 
      ...
    else
      ...
    end
    
    while x < 10 do
      ...
    end
    
    array = [14, 22, 34, 46, 92]
    for value in array do
      ...
    end
    
    • No Java-like for loop
  • Methods:
    • Factorial example:
      def fac(x)
        if x <= 0 then
          return 1
        end
        return x*fac(x-1)
      end
      
    • Default values for arguments:
      def inc(value, amount=1)
          value+amount
      end
      
    • Variable-length argument lists:
      def max(first, *rest)
        result = first
        for x in rest do
          if (x > result) then
              result = x
          end
        end
        return result
      end
      
    • Name-value arguments: use a hash.
    • Can omit curly braces to supply value for last argument:
      def create_widget(size, properties)
          ...
      end
      
      create_widget(6, {:id => "table22", :class => "Cart"})
      create_widget(6, :id => "table22", :class => "Cart")
      create_widget(6, id: "table22", class: "Cart")
      
    • Every method is associated with a class (default: private method of Object class).
    • Methods always return values:
      • Result of last expression
      • Default: nil.
    • Optional parentheses in method invocation (can be confusing)
      puts "Hello world"
      f (3+2)+1
      
  • Iterators, blocks, and yield:
    • Ruby makes it possible to define new control structures called iterators.
    • An iterator produces a series of values, separately from how they are processed.
    • Example:
      odd_numbers(3) do |i|
        print(i, "\n")
      end
      
      def odd_numbers(count)
        number = 1
        while count > 0 do
          yield(number)
          number += 2
          count -= 1
        end
      end
      
    • do ... end is a block: a chunk of code passed to the odd_numbers method.
    • Blocks usually have arguments: i
    • yield invokes the block passed to the method, passing it arguments.
    • Any method can be passed a block.
    • The same iterator can be used in many different situations.
    • A block is a closure: it has access to all variables visible at the point of its declaration
    • for loops are actually implemented as iterators; the following 2 loops have identical behavior:
      array = [14, 22, 34, 46, 92]
      for value in array do
        print(value, "\n")
      end
      
      
      array.each do |value|
        print(value, "\n")
      end
      
    • each is a method implemented by the Array class; it iterates over the elements of its array.
    • Can use for on any object that supplies an each method.
  • Classes:
    • Example:
      class Point
        def initialize(x, y)
          @x = x
          @y = y
        end
        
        def x
          @x
        end
        
        def x=(value)
          @x = value
        end
      end
      
      p = Point.new(3,4)
      puts "p.x is #{p.x}"
      p.x = 44
      
    • initialize method called during object construction
    • No direct access to instance variables: instead, define methods foo and foo=.
    • Methods can be public, protected, or private.
    • Inheritance: class Child < Parent
    • Every class inherits from Object
    • Duck typing:
      • No type-based restrictions on invoking methods
      • Can invoke a method foo on any object for which a foo method is defined.
  • Module: a group of methods that can be shared by many classes;
    • Similar to multiple inheritance or mixins.
    • Like the implementation of a Java interface.
    • Example: Enumerable
      • Your collection class implements each method.
      • Each object stored in the collection must implement the <=> operator (comparison).
      • Mix in the Enumerable module to get many additional methods.
        class MyClass
          include Enumerable
          ...
          def each
            ...
          end
        end
        
      • Example Enumerable methods:
        • min, max
        • sort
        • map: create a new collection by applying a block to each element
        • select: create a new collection of elements that meet certain criteria
  • Metaprogramming: programs that help you write programs
    • Reflection: like Java, can examine the state and structure of a Ruby program while it is running.
    • eval: can create new programs on-the-fly and execute them.
    • Can define new classes, methods (class_eval), and modules on-the-fly.
    • The following code automatically defines accessor methods for x and y:
      attr_reader :x :y
      
      Class definition is not compiled, it's executed.
    • method_missing: can catch invocations of undefined methods:
      • Define new methods "just-in-time" when invoked.
      • Simulate the existence of a large variety of methods.
    • And much more... facilities for domain specific languages, etc.