b r a y d e n . o r g / Software

/ WebHome / LanguagePages / RubyLanguage / RubyProgrammingExamples

This Web


WebHome  
Topic List  
Web Statistics 

All Webs


Books
Main
Random
Software
TWiki  

brayden.org


Home
Monthly Digest
Today's Links
Resumé
Reading List
Books RSS
Random RSS
Software RSS

Other


Dale's Blog

currently-reading
TextDrive

Ruby Programming Examples - a 3-line quicksort, really big numbers, code blocks, perl-like regular expressions, accessing command line parameters and the environment.

Ruby Programming Examples


See also: RubyExampleRandomNumbers RubyExampleStatistics RubyNumericalProgramming, RubyCoroutines, RubyBlogExtractor

Quicksort

I found this at comp.lang.ruby:

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


Fibonacci series

This sample illustrates:


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))}

The output (abbreviated) is:

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

Code Blocks

This sample illustrates:


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 ] )

The output is:

foo|12|bar|-1|

What just happened?

The makemap() procedure takes a code-block as an argument. If the final formal function parameter is preceded by an ampersand it is understood to be a code block. The function returns a procedure (a Proc object) that does nothing but iterate over its arguments, calling the supplied block on each element.

We can then define what that block should do: we call makemap() and pass a code block. Then when we call the returned procedure (using mymap.call(...)) we in fact call the original code block on each member of the argument passed to the call(...).

Regular Expressions

This sample is taken from the documentation, slightly modified.

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)

The output is:
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

Command-line and environment

This sample just shows how to access command-line arguments, the system environment, and the ruby environment.


#!/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}"}

Classes

This sample is copied directly from Programming Ruby and is included here simply as a reference to show the core features of ruby classes.


# 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

Timeouts

require 'timeout'
require 'socket'

timeout(20) {
    s = Socket::TCPSocket.new('google.com', 80)
    [...]
}

File / Directory Processing

I downloaded a zip file containing over a hundred C source files - unfortunately the filenames had all somehow been changed to upper case. So ...

# 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

 
 
Current Rev: r1.16 - 16 Jul 2004 - 21:28 GMT - DaleBrayden, Revision History:Diffs | r1.16 | > | r1.15 | > | r1.14
© 2003-2011 by the contributing authors.