A quick Ruby Tutorial, Part 4

Post on 08-Jan-2016

48 Views

Category:

Documents

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

A quick Ruby Tutorial, Part 4. COMP313 Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad Fowler, and Andy Hunt. 2 forms of assignment. hard coded to variables and constants: instrument = "piano" MIDDLE_A = 440 - PowerPoint PPT Presentation

Transcript

A quick Ruby Tutorial, Part 4

COMP313

Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad

Fowler, and Andy Hunt

hard coded to variables and constants:

instrument = "piano"

MIDDLE_A = 440

user defined for object attributes and other element references:

song.duration = 234

instrument["ano"] = "ccolo"

2 forms of assignment

problems

class BrokenAmplifier attr_accessor :left_channel, :right_channel def volume=(vol) left_channel = self.right_channel = vol end end

ba = BrokenAmplifier.new ba.left_channel = ba.right_channel = 99 ba.volume = 5 ba.left_channel → 99 ba.right_channel → 5

Parallel assignment

a = [1, 2, 3, 4]

b, c = a → b == 1, c == 2

b, *c = a → b == 1, c == [2, 3, 4]

b, c = 99, a → b == 99, c == [1, 2, 3, 4]

b, *c = 99, a → b == 99, c == [[1, 2, 3, 4]]

b, c = 99, *a → b == 99, c == 1

b, *c = 99, *a b == 99, c == [1, 2, 3, 4]

Nested assignment

b, (c, d), e = 1,2,3,4 → b == 1, c == 2, d == nil, e == 3

b, (c, d), e = [1,2,3,4] → b == 1, c == 2, d == nil, e == 3

b, (c, d), e = 1,[2,3],4 → b == 1, c == 2, d == 3, e == 4

b, (c, d), e = 1,[2,3,4],5 → b == 1, c == 2, d == 3, e == 5

b, (c,*d), e = 1,[2,3,4],5 → b == 1, c == 2, d == [3, 4], e == 5

Overwriting operators like + class Bowdlerize def initialize(string) @value = string.gsub(/[aeiou]/, '*') end def +(other) Bowdlerize.new(self.to_s + other.to_s) end def to_s @value end end a = Bowdlerize.new("damn ") → d*mn a += "shame" → d*mn sh*m*

defined? operator

defined? 1 → "expression"

defined? dummy → nil

defined? printf → "method"

defined? String → "constant"

defined? $_ → "global-variable"

defined? Math::PI → "constant"

defined? a = 1 → "assignment"

defined? 42.abs → "method”

defined? yield → ”yield” or nil

Case expressionsleap = case when year % 400 == 0: true when year % 100 == 0: false else year % 4 == 0 end case input_line when "debug" dump_debug_info dump_symbols when /p\s+(\w+)/ dump_variable($1) when "quit", "exit" exit else print "Illegal command: #{input_line}" end

another case examples

kind = case year

when 1850..1889 then "Blues"

when 1890..1909 then "Ragtime"

when 1910..1929 then "New Orleans Jazz"

when 1930..1939 then "Swing"

when 1940..1950 then "Bebop"

else "Jazz"

end

case as instanceof test

case shape

when Square, Rectangle

# ...

when Circle

# ...

when Triangle

# ...

else

# ...

end

For .. in .. looping

based on each => works for all classes defining eachclass Periods def each yield "Classical" yield "Jazz" yield "Rock" end endperiods = Periods.new for genre in periods print genre, " " end produces: Classical Jazz Rock

break, redo, next, retry

while line = gets next if line =~ /^\s*#/ # skip comments break if line =~ /^END/ # stop at end # substitute stuff in backticks and try again redo if line.gsub!(/`(.*?)`/) { eval($1) } # process line ... end

for i in 1..100 print "Now at #{i}. Restart? " retry if gets =~ /^y/end

Loops, blocks, and scope of variables

while/until/for do NOT create a new scope (!= java), but explicit code blocks can:

[ 1, 2, 3 ].each { |x| y = x + 1 } [ x, y ] --> error, but

x=nily=nil[ 1, 2, 3 ].each { |x| y = x + 1 } [ x, y ] --> [3,4]

Exceptions

op_file = File.open(opfile_name, "w") begin # Exceptions raised by this code will # be caught by the following rescue clause while data = socket.read(512) op_file.write(data) endrescue SystemCallError $stderr.print "IO failed: " + $! op_file.close File.delete(opfile_name) raise end

more on Exceptions

begin

eval string

rescue SyntaxError, NameError => boom

print "String doesn't compile: " + boom

rescue StandardError => bang

print "Error running script: " + bang

end

empty rescue catches StandardError instances,

may also use expression returning an Exception class

ensure (cf. Java’s finally)

f = File.open("testfile")

begin

# .. process

rescue

# .. handle error

ensure

f.close unless f.nil?

end

retry

@esmtp = true begin # First try an extended login. If it fails because the # server doesn't support it, fall back to a normal login if @esmtp then @command.ehlo(helodom) else @command.helo(helodom) end rescue ProtocolError if @esmtp then @esmtp = false retry else raise end end

Raising exceptions

raise raise "bad mp3 encoding"

raise InterfaceException, "Keyboard failure", caller

raise "Missing name" if name.nil?

if i >= names.size raise IndexError, "#{i} >= size (#{names.size})" end

raise ArgumentError, "Name too big", caller[1..-1]

Retry example

class RetryException < RuntimeError attr :ok_to_retry def initialize(ok_to_retry) @ok_to_retry = ok_to_retry end end

def read_data(socket) data = socket.read(512) raise RetryException.new(true), "transient read error” if data.nil? # .. normal processing end

Retry example cont.

begin stuff = read_data(socket)

# .. process stuff

rescue RetryException => detail

retry if detail.ok_to_retry

raise

end

Modules

• provide namespace against clashes• implement mixin facility

module Trig PI = 3.141592654 def Trig.sin(x) # .. endend

require 'trig' y = Trig.sin(Trig::PI/4)

Module mixin

• Modules are not classes, cannot have instancesmodule Debug def who_am_i? "#{self.class.name} (\##{self.object_id}): #{self.to_s}" end end class Phonograph include Debug # ... end

ph = Phonograph.new("West End Blues")ph.who_am_i? → "Phonograph (#937328): West End Blues"

Mixin interactionclass Song include Comparable def initialize(duration) @duration = duration end def <=>(other) self.duration <=> other.duration end end song1, song2 = Song.new(225), Song.new(260)song1 <=> song2 → -1 song1 < song2 → true song1 == song1 → true song1 > song2 → false

Resolving name clashes

methods: immediate class, thenlocal mixins last one first, thensuperclass, it’s mixins, etc …

variable before method name:a = 1def a 2enda -> 1a() -> 2

2 ways of IO

utility methods: gets, open, print, printf, puts, putc, readline, readlines, test

proper classes: IO, File, BasicSocket, …

endl = ”\n"

STDOUT << 99 << " red balloons" << endl

File IO examples

File.open("testfile") do |file| file.each_byte {|ch| putc ch; print "." }end

IO.foreach("testfile") {|line| puts line }

# read whole file into one string str = IO.read("testfile") str.length → 66 str[0, 30] → "This is line one\nThis is line " # read all lines into an array arr = IO.readlines("testfile") arr.length → 4 arr[0] → "This is line one\n"

StringIO

require 'stringio'

ip = StringIO.new("now is\nthe time\nto learn\nRuby!")

op = StringIO.new("", "w")

ip.each_line do |line|

op.puts line.reverse

end

op.string →”\nsi won\n\nemit eht\n\nnrael ot\n!ybuR\n"

Profiler

require 'profile' count = 0 words = File.open("/usr/share/dict/words")

while word = words.gets word = word.chomp! if word.length == 12 count += 1 end end

puts "#{count} twelve-character words”

Profiler output

% cumulative self self total time seconds seconds calls ms/call ms/call name 7.70 8.60 8.60 234938 0.04 0.04 IO#gets 7.65 17.14 8.54 234937 0.04 0.04 Fixnum#== 7.43 25.43 8.29 234937 0.04 0.04 String#length 7.18 33.45 8.02 234937 0.03 0.03 String#chomp! 0.70 34.23 0.78 20460 0.04 0.04 Fixnum#+ 0.00 34.23 0.00 1 0.00 0.00 Fixnum#to_s 0.00 34.23 0.00 1 0.00 0.00 Kernel.puts 0.00 34.23 0.00 1 0.00 0.00 File#initialize 0.00 34.23 0.00 2 0.00 0.00 Kernel.respond_to? 0.00 34.23 0.00 1 0.00 0.00 File#open 0.00 34.23 0.00 2 0.00 0.00 IO#write 0.00 34.23 0.00 1 0.00 0.00 Profiler__.start_profile 0.00 34.23 0.00 1 0.00 111640.00 #toplevel

Faster solution

require 'profile' words = File.read("/usr/share/dict/words") count = words.scan(/^.........…\n/).size puts "#{count} twelve-character words"

20460 twelve-character words % cumulative self self total time seconds seconds calls ms/call ms/call name 95.24 0.20 0.20 1 200.00 200.00 String#scan 4.76 0.21 0.01 1 10.00 10.00 File#read 0.00 0.21 0.00 2 0.00 0.00 IO#write

Duck typing

If it walks and talks like a duck, treat it like one (laissez-faire)

def append_song(result, song) result << song.title << " (" << song.artist << ")" end###### when checking, check for capability instead of class:def append_song(result, song) unless result.respond_to?(:<<) fail TypeError.new("'result' needs `<<' capability") end unless song.respond_to?(:artist) && song.respond_to?(:title) fail TypeError.new("'song' needs 'artist' and 'title'") end result << song.title << " (" << song.artist << ")”end

other ruby stuff

• threads and processes• GUI: Tk library• JavaDoc like Rdoc• Package management: RubyGems• Web stuff (including Ruby on Rails)• JRuby• Extending using host language (C/Java)

top related