jims ruby play
#!/usr/bin/env ruby
# Playing around with ruby.
# command line tools :
# $ ruby file
# $ irb # interactive
# $ rdoc file # create /doc folder from embedded comments
# $ gems # installation manager
# $ rails # web framework creator/manager
# $ rake # makefile tasky thing, in ruby
#
# scope classes and variable names :
# local global instance class constant/class_name symbols
# ---- ----- -------- ----- --------------- -------
# foo $size @height @@name MyClass :foo
# bar $a @color @@who PI :bar
#
# An expression like :foo returns a Symbol object, which is
# commonly used as hash keys and in class accessor definitions.
# Symbol to String conversions are built-in,
# i.e. :foo.to_s is "foo", while "foo".to_sym is :foo
# Note that file 'local' variables are *not* visible from within functions;
# if that's what you want, use @name, which adds that instance to 'self',
# (self is 'main' by default, but becomes <Object:**** > once you add
# instances :
# $ irb
# irb> self
# main
# irb> @x = 3
# irb> self
# <Object: 0x*** @x=3>
# arrays are similar to perl
# a = [ 1, 'cat', 3.14]
# print a[0]
# empty1 = []
# empty2 = Array.new
# b1 = %w{ ant bee cat dog elk } # word array
# b2 = ['ant', 'bee', 'cat', 'dog', 'elk'] # same
#
# so are hashes
# jim = {
# 'name' => 'Jim',
# 'phone' => 'xxx-yyyy'
# }
# print jim['name'] # like javascript
#
# block constructs all finish with 'end' keyword
# if a > 10
# puts "yes"
# else
# puts "no
# end
#
# perl-ish conditional modifiers are allowed
# puts "go" if a > 10
# square = square*square while square < 100
#
# regular expressionqs are much like perl
# if line =~ /P(erl|ython)/
# puts "line contains #{line}"
# end
# but with object-oriented stuff
# line.sub(/Perl/, 'Ruby') # replace first Perl with Ruby
# line.gsub(/Perl/, 'Ruby') # replace all Perl with Ruby
#
# code blocks either as
# do
# code.here()
# end
# or as
# {code.here()}
#
a = 3.0 # Floating point. 0 is required.
b = 'This is a string. ' # perl-lish single quotes
c = "a = #{a}. \n" # string interpolation and newline
print b + c
# Semi-colons are allowed at end of lines, but not required.
# The style guide I found suggested avoiding them.
#
# Line continuation can be explicit with \
# or implied by incomplete syntax.
#
# From wikipedia article on Ruby, under "gotchas" :
# * Uppercase/lowercase first letter in name implies constant/variable
# * $ (global), @ (instance), @@ (class) used for scope resolution
# * 3. isn't allowed; the period may be method invocation.
# * The only false booleans are [nil, false]; These are true: [0, "", []]
# * Methods return nil (not false) when they fail; a smalltalk-like convention
# * A construct "foo() until bar()" doesn't run foo() at all if bar() is true.
# * Constants are references, so changing what the object it points to is OK.
# * The 'and' operator does *not* bind tighter than 'or'.
# But ||, && work as usual.
# Define a subroutine (or function or method) that takes no args.
def foo
puts "This is foo." # puts (put_string) adds \n at end
end
foo # invoke foo method
foo() # same
def bar(x)
puts "In bar(x) with x='" + x.to_s + "'."
print " yield 1 : "
yield
print " yield 2 : "
yield
end
bar('hi') {puts "bar"} # invoke with "associated code block" - new concept
# Note that this "associated block" isn't actually a "parameter";
# control can pass back and forth between the two code segments.
bar('bye') do # another way to create a code block
puts "one"
puts "two"
end
# The code block can have args, which show up between vertical bars.
a = %w(one two three four) # array of words (like perl's qw()
a.each { |x| print x+" " } # iterate, applying given code block
puts # print newline
# This generalizes to pretty funky syntax forms.
# The following three lines prints '*****3456abcde'
5.times {print '*'}
3.upto(6) {|i| print i }
('a'..'e').each {|char| print char}
puts
# I/O is pretty typical.
# print "What is your input? "
# line = gets
# printf "You said '%s' \n", line
# gets also stores into global $_ which is default match, like perl
# # perlish printing lines that contain 'Ruby' :
# while gets
# if /Ruby/
# print
# end
# end
# # same with more Ruby styling, using ARGF input stream object
# ARGF.each { |line| print line if line =~ /Ruby/ }
# Classes (i.e. objects)
class Person # must start with uppercase
def initialize(name, haircolor) # special constructor method
@name = name # instance variables
@haircolor = haircolor
end
def to_s # override default to_s method
return "Person #{@name} has #{@haircolor} hair"
end
end
him = Person.new('Jim', 'black') # create an instance of Person object.
puts him.inspect # Print its representation.
# e.g. #<Person:0x26fe8 @haircolor="black", @name="Jim">
puts him.to_s # to_string method
# inheritance
class Child < Person
def initialize(name, haircolor, age)
super(name, haircolor)
@age = age
end
end
# The to_s method will be the first found in inheritance chain, i.e. Child's
kid = Child.new('Sally', 'blond', 4)
puts kid.to_s
class FixedChild < Person
def initialize(name, haircolor, age)
super(name, haircolor)
@age = age
end
def to_s
return super + " and is #{@age} years old"
end
end
# Now it has its own to_s which uses parent's.
kid2 = FixedChild.new('Tiny', 'red', 7)
puts kid2.to_s
# Note that we can "drop into" the Person class to add more stuff on the fly.
# This defines "attribute readers", namely accessors for some instance vars.
# Like JavaScript, all these classes are malleable at run time.
class Person
attr_reader :name, :haircolor, :lastname # getter, i.e. him.name
attr_writer :lastname # setter, i.e. him.lastname = 'x'
end
# In that example, the symbol :name is used to specifiy that
# an instance variable @name and its accessor should be created.
him.lastname = 'Smith' #
printf "The person named %s %s has %s hair.\n",
him.name, him.lastname, him.haircolor
# We can also create functions that act like accessors.
# The "pickaxe book" calls this "virtual attributes".
class Person
def fullname
"#{name} #{lastname}" # Note that 'return' is optional.
end
def fullname=(fullname) # what to do for him.fullname = foo
names = fullname.split
@lastname = names.pop
@name = names.join(' ')
end
end
puts "fullname was '" + him.fullname + "'"
him.fullname = 'Bob Jones'
puts "fullname is now '" + him.fullname + "'"
# Class variables have names that start with @@ .
# Class methods have names that start ClassName.*
# Here's an example with a counter for total number of instances.
class Tree
@@count = 0 # initialize class variable
def initialize(name)
@name = name
@@count += 1
end
def Tree.total
return @@count
end
end
trees = (1..5).map {|x| Tree.new(x.to_s)}
puts "Tree.total is " + Tree.total.to_s
puts "trees.length is " + trees.length.to_s
# You can preface method declaration blocks
# with keywords protected, private, public
# class Foo
# def meth1 # default public
# # ...
# end
# protected
# def meth2
# # ...
# end
# def meth3
# # ...
# end
# end
#
# initialize is private by default.
# Note that everything in Ruby is a reference, like in lisp.
foo1 = "This is foo"
foo2 = foo1 # another name for same object
printf "id of foo1 is %i \n", foo1.object_id
printf "id of foo2 is %i \n", foo2.object_id
# Modify last element of string.
foo2[-1] = "*" # modifications to foo2
puts "foo1 is now '#{foo1}'" # ... apply to foo1; same object.
# Ruby provides a .dup method to duplicate, and .freeze to prevent changes.
foo3 = foo1.dup
printf "id of foo3 is %i \n", foo2.object_id
foo3.freeze
# foo3[-1] = "*" # error, since foo3 has been 'frozen'
# Array slices have form array[index, count]
# returns new array of refs to the values in original array.
# On assignment, the count entries are replaced
# Ranges like 1..3 are also allowed, meaning (1,2,3),
# as well as 1...5 meaning (1,2,3,4) (three dots excludes ending value)
# and negative numbers, which count back from end.
# Lambda functions look something like this :
p = proc {|x| 3*x}
puts "p.call(5) is " + p.call(5).to_s
# Which can then be passed as associated code blocks.
def call_p(y)
yield(y)
end
def call2_p(y, *rest, &z) # *rest are optional args; &z is code block
z.call(y) # same
end
answer = call_p(7, &p)
puts "call_p(7) &p is #{answer}"
answer2 = call2_p(7, &p)
puts "call2_p(7) &p is #{answer2}"
answer3 = call_p(7) {|x| 3*x} # same
puts "call2_p(7) {...} is #{answer3}"
# Ruby has lisp-lish 'symbols' that start with :, i.e. :foo
# These are particularly common in hashes.
hash = { :name => 'Jim', :age => 17 }
# The perl-ish backticks run the expression in the shell.
# For a good time : Object.methods
# "parallel assignment" is allowed.
a,b = 1,2
# The * works here, too, in a 'shortcut' way.
a = [10, 11, 12, 13]
b, *c = a # c = [11, 12, 13]
# This can even be nested.
b, (c, d), e = 1, 2, 3, 4 # (c,d)=2; so d=nil, and e=3
# Ruby has the notion of a "module" analagous to perl's package;
# a file stypically starts with "module Foo" and
# then is "required" from other files.
# The module methods can be "mixed in" to classes like this :
module Debug
def whoAmI
return "#{self.class.name} (\##{self.id}): #{self.to_s}"
end
end
class Phonograph
include Debug # "mix in" the methods from Debug module.
# ...
end
# The 'self' keyword references the object that invoked the method.
# You can also modify the built-in classes on the fly :
class Array
def rotate!
self.unshift(self.pop)
return self[0]
end
end
# ... so the classes are very dynamic, like JavaScript's.
# Ducktyping is very Ruby.
# BEGIN { ... }, END { ... } are allowed and function same as perl's.
# ------------
#
#
# Ruby has a lot of variations on functions.
# And since it doesn't require parens to invoke a function,
# it's not obvious to see how to pass them around.
# In fact, usually ruby-ists use code blocks rather than
# pass functions.
#
# But coming from other languages, I'd be tempted to try this :
#
# # ------------- wrong
# def f(x)
# return 2*x
# end
#
# def g(z)
# return z(3)
# end
#
# g(f)
# # ---------------
#
# The problem here is that when it sees g(f),
# ruby will try to invoke f, and since there's no argument there,
# it'll just complain. g(f 3) would be g(f(3)), for example;
# without an arg it just sees g(f <no arg>).
#
# One way to do this is to use symbols, method(), and .call :
#
# irb> ff = method(:f) # here :f is the 'symbol' for f, which can access f().
# <Method: Object#f>
#
# This ff thing can be passed along as a parameter. And it can be invoked
# with ".call" , which will run f.
#
# So this works :
# ------------------ legal, though not very ruby-ish
#
# def f(x)
# return 2*x
# end
#
# def g(z)
# return z.call(3) # here z is a proc object
# end
#
# g(proc {|x| 3*x}) # creating a "lambda" proc object on the fly
#
# g(method(:f)) # passing a "method" proc object, that can call f(x).
#
# ------------------
#
# Another way to do this is with the .send method,
# which uses a symbol to invoke a method in a class.
#
# class Wrapper
# def f(x)
# return 2*x
# end
# end
#
# def g(z)
# return Wrapper.new.send(z, 3)
# end
#
# g(:f) # this sends :f to a Wrapper object, which then invokes f(3)
#
# --------------------
# or you could use eval() and put it in a string. Hmmm.
#
# --------------------
#
# # and arguably the most ruby-ish is to simply write g so that it takes a block:
#
# def g
# yield 3 # invoke the block
# end
# g {|x| 2*x} # pass the block
#
#
# or you can name the block in g's args, which then requires it,
# and treats it as a proc object which can be called.
#
# def g(&foo)
# foo.call(3)
# end
#
# g {|x| 3*x}
#
#