Ruby quick reference -------------------- general: object oriented, interpretted (#! /usr/bin/ruby) every statement is an expression and has a value variables: dynamically typed, not declared global: start with $ local: start with _ or lowercase letter class instance: start with @ class-wide: start with @@ alias x y # creates an alias x, y, z = 10, 20, 30 # parallel initialization constants: can actually be changed, start with uppercase letter literals: strings in single-quotes won't be interpretted (except \') while strings in double-quotes will be chars are just 1-char strings numbers can be integer or float, scientific or fixed point notation to specify a base use 0nnnn for octal 0bnnnn for binary 0x nnnn for hex (default is decimal) special values: false, nil: logical false true: logical true is anything other than false/nil ARGV: array of command line arguments $?: return value from last process, e.g. after res = `command` $0: name of this script $$: current process id .... many other of form $xxx .... including other files: require 'filename' operators: mostly C-like except missing ++, --, and uses ** for powers additional comparisons: x <=> y: -1 if x smaller, 0 if equal, 1 if y smaller x.eql?(y): true iff compatible type and equal x.equal?(y): true iff they're the same object strings: # strings in single quotes aren't intepretted except for \', e.g. puts 'hi there\n' # actually displays characters \n puts "hi there\n" # displays the \n as a newline # a wide variety of string operations are supported, e.g. s.length, s.chomp, s.upcase, s.downcase, s.swapcase, s.capitalize, s.center, s.rjust, s.ljust, s.strip, s.rstrip, s.lstrip, s.include?, s.index, s.start_with?, s.end_with?, etc # can access characters positionally, e.g. s[3] = "q" # can embed expression values in strings using #{ expr }, e.g. s = "x is #{x}" # can access a subrange or slice of characters, e.g. s[3..7] # grabs substring in positions 3 through 7 of s # can concatenate strings with the + operator, e.g. s = "hi " + "there" # can assign multiline text blocks easily with <<- notation (called a here block) myblock = <<-TheEnd ..... ..... ..... TheEnd # uses the TheEnd tags (or whatever name you chose) to delimit the start/end # can iterate through multiline strings using each_line, e.g. myblock.each_line do | curline | # do something with the current line (curline) end output: puts "blah blah blah #{x} blah \n" # embeds value of x file output: # open the file for writing, creating a handle fhandle = File.open("filename", "w") # write whatever you want into the file fhandle.puts("hi there") # close the file when done fhandle.close # note you can check if a file is readable/writable using if File.stat("filename").readable? ..... if File.stat("filename").writable? ..... input: x = gets # includes, the newline, can be removed with x = x.chomp file input: # open the file for reading, creating a handle fhandle = File.open("filename", "r") # read from the file, here shown one line at a time until end of file while !fhandle.eof? line = fhandle.readline # do whatever with the current line end # close the file fhandle.close run time type checking: type names include Integer, String, Array, etc if (x.kind_of?(NAMEOFTYPE)) then # actions selection examples: (note you can also put if/unless tests at the end of a block) if x < y then unless x < y then case x # actions # actions when val1,val2 then elsif x < z then else # actions # actions # actions when val3 then else end # actions # actions else end # actions ebd iteration examples: (iteration on arrays is shown in the array section) until x > y do begin begin # body # body # body end end until x > y end while x > y while x < y # body of loop end # iterate on values x through y for i in x..y do # body of loop, i is the current value in use end # iterate on values x+1 through y-1 for i in x...y do # body of loop, i is the current value in use end # iterate across a range of values (x..y).each do | i | # body of loop, i is the current value in use end array examples: # create an uninitialized array of size N arr = Array.new(N) # create and initialize an array arr [ 10, 20, 30 ] x = arr[i] y = arr.shift # shifts leftmost element out of arr into x arr << z # push z onto end of array # array copies are shallow, so modifying a copy modifies the original, e.g. arr1 = [ 1, 2, 3] arr2 = arr1 arr2[0] = 100 # now arr1 is also [ 100, 2, 3 ] # iterate across elements of array arr.each do | e | # use current element e end # c-style for loop across array elements, using the length method for i in 0..arr.length()-1 do # use array element arr[i] end # iterating that works on a single item (non-array) as well as an array [*content].each do | elem | # if content is an array it iterates, if content is an item puts "#{elem}" # embeds the content into a single-element array end hash examples: # hashes are collections of key-value pairs, e.g. colours = { # key => value "red" => 0xFF0000, "green" => 0x00FF00, "blue" => 0x0000FF, "white" => 0xFFFFFF, } # you can access values using [], e.g. x = colours['red'] # you can set default values, e.g. colours.default = 0x888888 # you can add or delete new pairs at any time colours['black'] = 0x000000 colours.delete('white') colours.clear() # deletes all the key-value pairs # you can test if a key or value exists in the hash, e.g. if (colours.has_key?('white')) then .... if (colours.has_value?(0x000000)) then .... # you can iterate across the key value pairs, e.g. colours.each do | key, value | # do something with current key and value end # you can swap the keys and values colours.invert() methods: can be defined globally (like a function) or within classes # basic method definition, # note that the return value is by default the value # of the last statement in the method def f(x, y) res = x * y # returns value of res end # can support default values for parameters, e.g. def f(x=1, y=10) # body end # can support variable number of parameters, e.g def f(i, j, *r) # r is an array of any arguments after the first two end exception handling # if an exception is raised in a block of code we # can catch it and respond using a rescue block, e.g. begin x = 1 y = 0 z = x / y # cause a divide by zero exception puts "#{x} / #{y} is #{z}" rescue begin puts "something went wrong" puts "bet it was divide by zero" end puts "bye now!" end # regular expressions # we can store regular expressions in a string and see if other # strings match that expression, e.g. # test if str is an integer def isInt(str) intex = '^[0-9]+$' # pattern for an int if str.match(intex) then puts "#{str} is an integer" else puts "#{str} is not an integer" end end # test a variety of cases isInt("123") isInt("123x") isInt("x123") isInt("x123y") isInt("xy") # regex symbols # the regular expression is typically enclosed in / / # ^ and $ match the beginning and ending of a line # \b matches a word boundary # [i-n] matches any char from i through n # + matches one or more, * matches 0 or more, ? matches 0 or 1 # {2,9} matches 2 to 9 # use \ to escape special characters # ( ) can be used to group blocks to quantify with + * ? {} # search and replace # many ruby functions also use regular expressions to find or alter # string contents, e.g. # use .sub to get a copy of a string, replacing one old pattern with a new pattern, # or .gsub to replace all copies of the old pattern with the new pattern str = "blah bxxxx foo" s = str.sub(/[x]+/, "Q") # replace the first sequence of x's with a single Q puts "#{str} became #{s}" special begin/end blocks: # can specify a block of code that runs before anything else in the script BEGIN { # early code } # can specify a block of code that runs after the rest of the script END { # late code } classes: names begin with uppercase letter, fields are private # define the class, fields, and method class Fraction # instance variables are available to methods for the current instance # fields for numerator and denominator @numerator = 0 @denominator = 1 # class variables are shared across all instances @BadDenom = 0 # the initialize method is used when a new instance is created, # this one sets the numerator and denominator as requested def initialize(num, denom) @numerator = num @denominator = denom end # method to return the value of the fraction def curValue() return @numerator/@denominator end end # end the class definition # inheritance from another class is done using <, e.g. class SubFraction < Fraction ..... end # create a new instance of a fraction and look up its value f = Fraction.new(12,16) v = f.curValue() # at any time you can add more methods or fields to the class, e.g. class Fraction def print() puts "#{@numerator} / #{@denominator} \n" end end # you can also remove a method from a class, e.g. class Fraction remove_method :print end # you can obtain a list of all the methods of the class using Fraction.instance_methods(false) # here not including inherited methods Fraction.instance_methods(true) # here including inherited methods # for any field in the class, you can get ruby to auto-generater accessors # for that field (methods to get or set the field value), this is done using # attr_accessor, e.g. class Fraction attr_accessor :numerator end # now you can look up or set the value of the field, e.g. f.numerator = 3 n = f.numerator() # you can also create methods that can be called without needing # an instance of the class (like File.open() or Array.new) class Fraction def Fraction.foo ... end end modules: much like namespaces in other languages # if you want to use the same constants, variables, or class methods/fields # for different things at different times, you can put their definitions # into different modules # you then specify which one you want to use at any given point in time # by specifying which module you want to use at that time # define one module for rational number computations, another for real number computations module ActRational def div(n, d) return Rational(n/d) end end module ActReal def div(n, d) return (n/d.to_f) end end # create a Fraction class that uses the real number versions class Fraction @num = 0 @den = 1 include ActReal def initialize(n, d) @num = n @den = d end def value return div(@num, @den) end end # test out the results f = Fraction.new(5,6) x = f.value puts "result is #{x}" # you can test if a module has been included using include?, e.g. if include?(RealBehaviour) then ...... end ------------------------------------------------------------------------------------------ # ============================== # Higher order functions in Ruby # ============================== # passing a block of code to a function (using yield) ===================================================== # function f takes parameters x and y, # plus a block of code to be executed by its yield call def f(x, y, z) puts "f passed #{x}, #{y}, #{z}" c = x + y return yield(c, z) # call the block of code, passing parameters c and z end # now call function f, passing the three parameters and the block of code # note that the block of code itself can accept parameters result = f(1, 10, 100) { | i, j | puts "block passed #{i}, #{j}"; i + j } puts "Result is #{result}" # evaluating text as code with eval # ================================= # eval takes ruby code in text form and evaluates it, e.g. str = "4 * 21 / 3" x = eval(str) puts "#{str} = #{x}" # you can seperate statements with the ; # e.g. here a string contains a function definiton, # we evaluate the string and then run the function str = "def hello; puts \"hello!\"; end" x = eval(str) # you can specify a binding (environment) to use when evaluating the string, # i.e. with access to the local variables/environment of the component you specify # here we'll set up an inherited method, bindings, so you can use the bindings of any class class Object def bindings binding # returns the binding for the class in question end end # now create an instance of a class, and evaluate a string using # the environment of that instance i = MyClass.new eval(cmd, i.bindings) # eval can actually take four parameters: the string, the binding, a filename, and a line number # note built in variables __FILE__ and __LINE__ give you the current filename, line number eval(cmd,binding,__FILE__,__LINE__) # applying a block of code to every element of an array # ===================================================== # map is the method to apply to the array, taking the block of code as a parameter, # here we negate each element of the array arr = [ 1, 2, 3 ] arr = arr.map { |element| -element } # print the array contents puts "array used to be [ 1, 2, 3 ]" print "now it is " arr.each do | e | print "#{e}," end print "\n" # applying a method to a list of values # ===================================== # sometimes the parameters we want to use in a method call # are contained in a list, e.g. we have a list with values # [ 1, 2, 3 ] and what we want is to call foo(1, 2, 3) # this is done using the * operator, e.g. def foo(a, b, c) puts "foo: #{a}, #{b}, #{c}" end args = [ 1, 2, 3 ] foo(*args) # running reduce on a sequence of operands # ======================================== # reduce applies a binary operator in sequence to an array of values, # syntax: array.reduce(initialValue, :operator) # at each step it applies the operator the current computed value # and the next value in the sequence, giving a new computed value arr = [ 1, 2, 3, 4 ] sum = arr.reduce(0, :+) puts "The sum of 1,2,3,4 is #{sum}" # in this case it computed the sequence (((0+1)+2)+3)+4 # generating multiple methods from a template # =========================================== # creating a method that uses a template to generate a set of customized # variants of a method class Expandable # names of the methods to create, each one prints that string and an argument variants = [ :hi, :bye, :hmmm ] variants.each do | methodName | define_method methodName do | arg | puts "#{methodName} #{arg}" end end end # create an instance of the class and call one of the variants test1 = Expandable.new test1.hi("there") # creating/adding an entirely new method to a class as the program runs # ===================================================================== # creating a class that can dynamically receive new methods class Expandable # addMethod lets us specify the name and code of the method # to be created def addMethod(methodName, &codeBlock) # it works by doing a runtime call to itself (using send) # of define_method(methodName, &codeBlock) # (send is needed because the define_method is private) self.class.send(:define_method, methodName, &codeBlock) end end # create an instance of the expandable class test2 = Expandable.new # call addMethod, adding a sayHi method and then calling it test2.addMethod(:sayHi) { puts "Hi!" } test2.sayHi # create/add an entirely new method to a class using eval class Expandable eval("def myNewMethod; puts \"body goes here\"; end") end # you can add instance/class methods using eval, e.g. # instance_eval runs the code as if we were inside a method of that specific object # e.g. x.instance_eval(str) # class_eval runs the code for the class, # e.g. MyClass.class_eval(str) MyClass.class_eval("def foo; puts \"body goes here\"; end") # invoke using someObj.foo MyClass.instance_eval("def foo; puts \"body goes here\"; end") # invoke using MyClass.foo # define_method vs eval # ===================== # just as an aside, eval itself is very slow, # but the methods it creates run more efficiently than those # created with define_methods # which one you choose depends on where you prefer your efficiency: # when creating the methods, or when running the methods # evaluating a block of code in the context of a class or an instance # =================================================================== # basic class for categorizing types of vehicle, each has a type and a name class Vehicle @@vTypes = [ 'unknown', 'aircraft', 'watercraft', 'landcraft' ] @vType = 'unknown' @vName = 'unknown' def initialize(vehicleType, name) @vType = vehicleType @vName = name end end # evaluate def printInstance in the context of the Vehicle class, # which effectively creates an instance method Vehicle.class_eval do def printInstance puts "instance" end end # evaluate def printClass in the context of an intancerint # information about a specific instance of the class Vehicle.instance_eval do def printClass puts "class" end end # create an instance of vehicle and try out both new methods b = Vehicle.new("aircraft", "blimp") b.printInstance Vehicle.printClass