Ruby Programming Examples - a 3-line quicksort, really big numbers, code blocks, perl-like regular expressions, accessing command line parameters and the environment.
def qsort1(arr)
return [] if arr.length <= 0
x, *xs = arr
qsort1(xs.select{ |y| y <= x } ) + [x] + qsort1(xs.select{ |y| y > x } )
end
I think that the number of recursive calls could be cut in half as follows:
def qsort2(arr)
return arr if arr.length <= 1
x, *xs = arr
qsort2(xs.select{ |y| y <= x } ) + [x] + qsort2(xs.select{ |y| y > x } )
end
And since qsort works best on large arrays we can also try this:
def qsort3(arr)
return arr if arr.length <= 1
return arr.sort if arr.length <= 5
x, *xs = arr
qsort3(xs.select{ |y| y <= x } ) + [x] + qsort3(xs.select{ |y| y > x } )
end
But then we can ask - what if we just used the built-in Array#sort method? Well, here's my summary of results, using the above 3 methods each called 10 times on an input of about 1500 lines of text (a 95K file). In qsort3 I changed the value '5' to '2000' to ensure that only Array#sort would be called:
| method | time (secs) |
|---|---|
| qsort1 | 3.205 |
| qsort2 | 2.964 |
| qsort3 | 0.07 |
def fibonacci(n)
return 1 if n < 2
prev, ret = 1, 1
2.upto(n) { prev, ret = ret, prev + ret }
return ret;
end
0.upto(100) {|i| printf("%d -> %d\n", i, factorial(i))}
0 -> 1 1 -> 1 2 -> 2 3 -> 3 4 -> 5 5 -> 8 6 -> 13 7 -> 21 8 -> 34 . . . 97 -> 135301852344706746049 98 -> 218922995834555169026 99 -> 354224848179261915075 100 -> 573147844013817084101
def makemap(&aBlock)
return proc do |x|
x.each {|i| aBlock.call(i)}
end
end
mymap = makemap() { |y| print y,"|" }
mymap.call([ 'foo', 12, 'bar', -1 ] )
foo|12|bar|-1|
mymap.call(...)) we in fact call the original code block on each member of the argument passed to the call(...).
def showRE(a,re)
if md = re.match(a)
"#{re.source}: #{md.pre_match}((#{md[0]}))#{md.post_match}"
else
"no match for ((#{re.source})) in #{a}"
end
end
puts showRE('The skinny on regular expressions', /e(.*)e/i)
puts showRE('Marian Williams', /(\w+)\1/)
puts showRE("Line 1\nLine 2\n",/e.*$/)
puts showRE("Line 1\nLine 2\n",/e.*$/m) # multiline mode
puts showRE('A man a plan a canal Panama',/(\w)(\w).*\2\1/i)
e(.*)e: Th((e skinny on regular expre))ssions (\w+)\1: Marian Wi((ll))iams e.*$: Lin((e 1)) Line 2 e.*$: Lin((e 1 Line 2)) (\w)(\w).*\2\1: A ((man a plan a canal Panam))a
#!/bin/ruby
require "rbconfig.rb"
include Config
puts "---- Ruby program and command-line arguments"
puts $0
puts ARGV.length
ARGV.each {|a| puts a}
puts "---- Environment Variables"
ENV.each {|k, v| puts "#{k}=#{v}"}
puts "---- Specific Environment Variables"
puts "OS=#{ENV['OS']}"
puts "---- The Ruby Configuration"
CONFIG.each {|k, v| puts "#{k}=#{v}"}
# copied from Programming Ruby
class Song
@@plays = 0 # class variable
def initialize(name, artist, duration) # constructor - called by Song.new
@name, @artist, @duration, @plays = name, artist, duration, 0
end
def to_s
"Song: #{@name}--#{@artist} (#{@duration})"
end
attr_reader :name, :artist, :duration, :plays # symbols / property accessors
def Song.plays # class method
@@plays
end
def duration=(newDuration) # property setter
@duration = newDuration
end
def durationInMinutes
@duration / 60.0 # force floating point
end
def durationInMinutes=(value)
@duration = (value*60).to_i
end
def play
@@plays += 1
@plays += 1
end
end
class KaraokeSong < Song
def initialize(name, artist, duration, lyrics)
super(name, artist, duration)
@lyrics = lyrics
end
def to_s
super + " [#{@lyrics}]" # note the streamlined syntax
end
attr_reader :lyrics
attr_writer :lyrics # property setter
end
class SongList
MaxTime = 5*60 # 5 minutes - a constant
def SongList.isTooLong(aSong)
return aSong.duration > MaxTime
end
end
# Example of a singleton class
# This also shows how to prevent AClass.new
class Logger # a singleton (non thread-safe version)
private_class_method :new
@@logger = nil
def Logger.create
@@logger = new unless @@logger
@@logger
end
end
#------------------------------- Tests
if $0 == __FILE__
aSong = Song.new("Bicylops", "Fleck", 260)
puts "Inspect=" + aSong.inspect
puts "to_s="+aSong.to_s
puts "artist="+aSong.artist
aSong.duration = 270
puts "to_s="+aSong.to_s
aKSong = KaraokeSong.new("My Way", "Sinatra", 225, "And now, the...")
puts "karaoke.to_s="+aKSong.to_s
puts "artist="+aKSong.artist
aKSong.lyrics = "And now, the time is near ..."
puts "karaoke.to_s="+aKSong.to_s
puts "Changing duration ..."
aKSong.durationInMinutes = 4.2
puts "karaoke.to_s="+aKSong.to_s
aSong.play; aKSong.play; aSong.play
aSong.play; aKSong.play; aKSong.play
aSong.play; aKSong.play; aKSong.play
puts "#{aSong.name} played #{aSong.plays} times"
puts "#{aKSong.name} played #{aKSong.plays} times"
puts "Total #{Song.plays} songs played"
end
require 'timeout'
require 'socket'
timeout(20) {
s = Socket::TCPSocket.new('google.com', 80)
[...]
}
# set all filenames in a directory to lower case
begin
Dir.chdir(ARGV[0])
Dir.foreach(".") {|x| File.rename(x, x.downcase) if File.ftype(x) == "file" }
rescue => someTrouble
puts "Exception: #{someTrouble}"
end
-- DaleBrayden - 21 Aug 2002