310 likes | 404 Views
Dive deep into Ruby with syntax, naming conventions, symbols vs. strings, hashes, classes, methods, and more. Understand closures, mixins, and the power of symbols in Ruby programming. Get ready to level up your Ruby skills!
E N D
Things to Review • Syntax & conventions • Class methods/variables vs. Instance methods/variables • Mix-ins and Modules • Closures, Yield, and Blocks
CS61A Keywords of the Day • Symbol • Environment
Review: Naming Conventions & Syntax • ClassNames class NewRubyProgrammer ... end • method_names and variable_names def learn_conventions ... end • predicate_like_methods? def is_faculty_member? ... end • dangerous_methods! def brainwash_with_ruby! ... end • :symbols favorite_framework = :rails • Symbol or string? use to_sym and to_s to convert between. • SOME_CONSTANTS or OtherConstants • result in warning if reassigned after init
Review: Syntax • Syntax features • Whitespace is not significant (unlike Python) • Statements separated by semicolons or newlines • Statement can span a newline* • Parentheses can often be omitted* • when unambiguous to parser; use caution!! • raise "D'oh!" unless valid(arg) • raise "D'oh!" unless valid arg • raise "D'oh!"unless valid(arg) • Advice: use a good text editor
Review: Hashes h = {"key" => 1, :value => "foo" } h.has_key?("key") => true h["not a key"] => nil (not an error!) h.delete(:value) => {"key" => 1 } h.merge( {:key2 => "3", "hi" => :blah} )=> {"key"=> 1, "key2" => 3, “hi” => :blah} • Ruby & Rails idioms • omitting braces when a function takes a hash as its last argument • omitting parens around function arguments link_to "Edit student", :controller=>'students', :action=>'edit' link_to("Edit student", {:controller=>'students', :action=>'edit'}) • Warning! if ambiguous parse...better safe than sorry!
Administrivia Break • 5um signup • Lab 0 • Self-diagnostic quiz on web page • Books - see course homepage too • more or less required: Agile Web Development with Rails, 2nd ed. • Programming Ruby (free, online at rubycentral.org/book) • or The Ruby Way by Fulton
Reminder: Using ri and irb from the shell • irb, interactive Ruby interpreter • follow along with the examples! • ri (Ruby info) is like man • ri Comparable • ri gsub • ri String#gsub • Note, need to be in a shell that has PATH and environment variables set correctly • See www.ruby-doc.org for more good documents
Everything is an object; (almost) everything is a method call • Everything is an object • Even integers (try 57.methods) • Even nil (try nil.respond_to?(:to_s)) • (almost) every “operator” is really a method call • my_str.length => my_str.send(:length) • mymethod(“foo”) => self.send(:mymethod, “foo”) • 1 + 2 => 1.send(:+, 2) • arr[4] => arr.send(:[ ], 4) • arr[3] = “foo” => arr.send(:[ ]=, 3, “foo”) • if (x == 3) => if (x.send(:==, 3))
Nanoquiz • What happens? x="foo" puts 5+x puts "5"+x
Classes & Methods # Methods for MyBank.com class Account @@bank_name = "MyBank.com" # constructor is always called initialize def initialize(starting_balance=0) @balance = starting_balance end # instance methods def balance @balance end def deposit(amount) @balance += amount end def withdraw(amount) @balance -= amount end # A class method def self.bank_name @@bank_name end end
Instance methods, not instance variables • Let’s try a few... my_account.@balance my_account.balance my_account.balance = 100 @@bank_name other_account = Account.new(0) other_account.bank_name Account.bank_name • ...got it?
Instance variables: shortcut class Foo def initialize(bar,baz) @bar,@baz=bar,baz end def bar ; @bar ; end def baz ; @baz; end def bar=(newbar) ; @bar=newbar ; end def baz=(newbaz) ; @baz=newbaz; end end
Instance variables: shortcut class Foo def initialize(bar,baz) @bar,@baz=bar,baz end attr_accessor :bar, :baz end
Pitfall class Account def empty_1 balance = 0.0 # warning!! end def empty_2 @balance = 0.0 # OK end def empty_3 self.balance = 0.0 # also ok, but different end end
REMEMBER! • a.b means: call method b on object a • a is the receiver to which you sendthe method call, assuming a will respond tothat method • does not mean:b is an instance variable of a • does not mean: a is some kind of structure of which b is a member Understanding this distinction will save you from much grief and confusion
There are (almost) no Loops • “Objects manage their own traversal” • (1..10).each {|x| ... }=> range traversal • my_array.each {|elt| ... }=> array traversal • hsh.each_key {|key| ... }hsh.each_pair {|key,val| .. }=> hash traversal • 10.times {...} => iterator of arity zero • 10.times do ... end {...} is a synonym for do...end
Iterators • Consider a simple linear array... • you can enumerate its elements • you can perform operations on the collection (find, delete_if, sort elements, etc.) • Now consider a BinaryTree data structure • instance vars: left child, right child • each child is either a BinaryTree or something else—a leaf node • in principle, should support many of same operations as array!
Ruby-think • How would you go about coding a library to support operations like find, delete_if, sort, etc.? • Hint 1 (bad): how does sort() library function work in ANSI C? • Hint 2 (good): what properties of an Array allow you to do these things? • it’s enumerable • its enumerability gives rise to all the other stuff • Can we do that in a generic way for a BinaryTree as well?
Ruby-think, cont. • The problem in thinking imperatively is that we don’t know what we will want to do with the elements when we enumerate them. • An iterator separates the concept of “enumerate the elements” from the concept of “doing something” inside that loop.
Here’s how to use it... Block of arity 1 "Armando" "Will" "Arthur" • The iterator yields a value to the block • The block executes in the environment in which it was originally defined • The block is called a closure
How about sorting, find, ...? • Simple! just include the module Enumerable • Module methods get mixed in to BinaryTree class • Contract: module expects BinaryTree objects to respond_to:each, and it provides the other methods • If you want sorting, includeComparable and make sure the things to be compared respond_to :<=>
Mix-in example: Comparable • Example: Define <=> method for your class • include Comparable • methods in Comparable get mixed in (added to) the target class that did the include • methods in Comparable assume that objects of target class respond to <=> • doesn’t actually matter what the class is! • Now, get < <= => > == between? for free • and, your class can now be sorted (by mixing in Enumerable...what do you suppose it assumes?) • Enumerable also provides all?, any?, collect, find, include?, inject, map, partition, ....
Duck Typing Makes it Possible • Ruby type = set of values + set of operations • A ruby module defines... • a collection of behaviors • that depend only on the presence of one or more specific existing behaviors • i.e.: If it looks like a duck and walks like a duck=> it responds to the same methods as a duck. • Note, a module ≠ a class • but module methods can get mixed in to classes
Blocks • Iterators are just one use of closures • For other interesting ones, check out: • open method of File class • form_tag helper method in Rails • Exercise: implement times in a module n=3 n.times { print "Ho" }=> HoHoHo How would you go about this?
Summary: Ruby’s Distinguishing Features • Object-oriented with single inheritance • everything is an object • almost everything is a method call • Modules play a role similar to Java’s interfaces and enable “duck typing” • Dynamically typed • Objects have types; variables don’t • very few operators in the language; most are defined as instance methods on objects • Idiomatically, {} and () sometimes optional
Variables & Methods • Variables have no type; objects do • variables spring into existence on first assignment • nil,false are Boolean false; everything else true • Everything* is passed-by-reference • can use clone method to effect pass-by-value *except Fixnums... • Defining methods def foo(x); [x,x+1]; end def foo(x) [x,x+1] end def foo(x) return [x,x+1] end
Arrays x = [3, 'a', "third", :blah, :last] x[0] => 3 x[-1] => :last x[-2] => :blah x[-3..-1] => ["third", :blah, :last] y = [1,2] y += [3,4] => [1,2,3,4] y << 5 => [1,2,3,4,5] y << [6,7] => [1,2,3,4,5,[6,7]] • Note! These are nearly all instance methods of Array—not language operators!
Hashes and function calls • Immediate hash (any object can be a key, any object can be an attribute) my_hsh = {:foo => 1, "x" => nil, 3 => ['a',4]} my_hsh[:nonexistent_key]returns nil • Parens can be omitted from function calls if parsing is unambiguous x = foo(3, "no") x = foo 3, "no" • Braces can be omitted from hash if parsing is unambiguous x = foo( {:a=>1,:b=>2}) x = foo(:a=>1,:b=>2) • easy way to do keyword arguments • Caveat: passing immediates to a function that accepts multiple hashes as its arguments