.. vim: set ft=rst:
=====================
Ruby Course Notes
=====================
:author: Dave Kuhlman
:contact: dkuhlman (at) davekuhlman (dot) org
:address:
http://www.davekuhlman.org
:revision: 1.1.1
:date: |date|
.. |date| date:: %B %d, %Y
:Copyright: Copyright (c) 2013 Dave Kuhlman. All Rights Reserved.
This software is subject to the provisions of the MIT License
http://www.opensource.org/licenses/mit-license.php.
:Abstract: This document provides notes and an outline of an
introductory course on programming in Ruby.
.. sectnum::
:depth: 4
:start: 1
.. contents::
:depth: 4
Introductions Etc.
====================
Resources
-----------
Look here for help:
- `Ruby home --
http://www.ruby-lang.org/
`_
- `Ruby documentation --
http://www.ruby-lang.org/en/documentation/
`_
Preliminaries
---------------
Text editor -- Use any good programmer text editor. See:
- http://en.wikipedia.org/wiki/Text_editor
- http://en.wikipedia.org/wiki/Comparison_of_text_editors
The Ruby interactive shell -- irb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some notes:
- ``irb`` can be configured from the command line or in your
personal configuration file ``.irbrc`` in your home directory.
- For help with configuring ``irb``, see chapter
"Interactive Ruby Shell" in
`Programming Ruby
The Pragmatic Programmer's Guide --
http://www.ruby-doc.org/docs/ProgrammingRuby/
`_.
Or, (on Linux), use ``man``::
$ man irb
- For a more simple prompt, try::
$ irb --prompt=simple
Or, put the following in your ``~/.irbrc`` file::
IRB.conf[:PROMPT_MODE] = :SIMPLE
- If tab completion is not working for you, try adding the following
to your ``~/.irbrc`` file::
require 'irb/completion'
My ``.irbrc`` also saves history across sessions. Here is my
complete ``.irbrc``::
IRB.conf[:PROMPT][:SIMPLE_DAVE] = { # name of prompt mode
:PROMPT_I => ">> ", # normal prompt
:PROMPT_C => ">> ", # prompt for continuated statement
:PROMPT_S => nil, # prompt for continuated strings
:RETURN => " ==>%s\n" # format to return value
}
#IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
require 'irb/completion'
require 'irb/ext/save-history'
require 'pp'
# Make ruby ``print`` act more like Python ``print``.
# Set the field separator and the record separator special variables.
$, = " "
$\ = "\n"
def dir obj
puts "Class: #{obj.class} object_id: #{obj.object_id}"
puts "Methods:", obj.methods.sort.inspect
end
Displaying values, variables, etc -- Use ``puts``, ``print``, and
``p``:
- ``puts`` adds a newline.
- ``p obj`` writes ``obj.inspect`` and a newline. When you are
debugging code, it's likely that it's ``p`` that you want.
- ``print`` does *not* add a newline.
- You can modify the behavior of ``print a, b, c`` by setting the
values of ``$,`` and ``$\\`` (the field separator and the record
separator variables). Example::
>> $, = "::"
==>"::"
>> $\ = "\n-----\n"
==>"\n-----\n"
>>
>>
>> print 11, 22, 33
11::22::33
-----
==>nil
>>
Or, it might be more useful to set the field and record separators
as follows::
>> $, = " "
==>" "
>> $\ = "\n"
==>"\n"
>> print 11, 22, 33
11 22 33
==>nil
>>
You can do this in your ``.irbrc`` file.
- If you start ``irb`` with this command ``$ irb -rpp``, then you
can use ``pp obj`` to pretty print objects.
- I do this ``print some_obj.methods.sort`` *a lot*. If you are
like me in that way, you can consider adding something like the
following to your ``.irbrc`` file, so that it will always be
available at the ``irb`` interactive prompt::
def dir obj
puts "Class: #{obj.class}"
puts "Methods:", obj.methods.sort.inspect
end
Then use ``dir(some_object)`` (with or without parentheses) to
display the methods supported by an object.
Running a Ruby script
~~~~~~~~~~~~~~~~~~~~~~~
Run and test a script with one of the following. The second form
enables you to use the Ruby debugger::
$ ruby my_script.rb ...
$ ruby -rdebug my_script.rb ...
Also see `Running Ruby scripts`_.
Additional help and documentation
-----------------------------------
Here are some places and ways to get help:
- If the Ruby information system is installed on your system, you
can get help from the command line. For example::
$ ri Array.sort
= Array.sort
(from ruby core)
o
o
o
- From the document you are currently reading.
- At the Ruby documentation Web site:
http://ruby-doc.org/
- Documentation on Ruby's standard classes is here:
http://ruby-doc.org/core-2.0/index.html
- "Programming Ruby The Pragmatic Programmer's Guide":
http://www.ruby-doc.org/docs/ProgrammingRuby/
- "Ruby programming" at the Wikibooks Web site:
http://en.wikibooks.org/wiki/Ruby_Programming
- "The Ruby User's Guide":
http://www.rubyist.net/~slagell/ruby/index.html
- "Ruby Essentials":
http://www.techotopia.com/index.php/Ruby_Essentials
Running Ruby scripts
----------------------
Run a Ruby script as follows::
$ ruby my_script1.rb
$ ruby my_script2.rb arg1 arg2 arg3
On Linux and Unix-like systems, to make the script itself
executable, do the following:
1. Add the following as the first line of the script::
#!/usr/bin/env ruby
2. And, change the permission of the file to executable. To do so,
either use your file manager, or from the command line do
something like this::
$ chmod u+x my_script.rb
In order to make your script both "run-able" and usable as a library
(with ``require``), use the following at the bottom of the script to
start processing when it is run, but *not* when it is imported (with
``require``)::
if __FILE__ == $0
main()
end
Notes:
- The above example assumes that when you run this script, you want
to call the function ``main``.
- The command line arguments are available within your script in the
pre-defined variables ``ARGV`` or ``$*``.
- If you need to parse and process command line options (prefixed
with "-" or "--"), consider using the ``optparse`` module.
- In order to use the Ruby debugger, run your script with the
``-rdebug1`` flag, for example::
$ ruby -rdebug my_scipt.rb
Also see `Debugging ruby scripts`_
Inspecting and examining Ruby objects
---------------------------------------
The following may be helpful::
>> p obj
>> puts obj.methods.sort.inspect
And, you could consider adding the following definition to your
``.irbrc`` file::
# A quick way to display object info and a sorted list of its methods.
def dir obj
puts "Class: #{obj.class} object_id: #{obj.object_id}"
puts "Methods:", obj.methods.sort.inspect
end
Viewing Ruby documentation with ``ri``
----------------------------------------
The ``ri`` command line documentation viewer is a very useful way to
learn about Ruby modules, classes, and their methods.
If it is not already installed, do this::
$ sudo gem install rdoc-data
$ rdoc-data --install
Then you should be able to display documentation by doing things
like the following::
$ ri Array
$ ri Array.each_with_index
$ ri String.slice
And, if the item for which you want documentation is in a module,
try things like this::
$ ri Asciidoctor::Document
$ ri Asciidoctor::Document.blocks
Note: The above works on my machine because I have ``AsciiDoctor``
installed. ``AsciiDoctor`` is written in Ruby, by the way.
And, if you want an interactive prompt with tab name completion,
then run::
$ ri -i
For more help with ``ri`` and ``rdoc``, see:
http://jstorimer.com/ri.html
Lexical matters
=================
Lines
-------
Ruby is line oriented. Typically you will write one Ruby statement
per line.
You *could* write more than one statement on a single line separated
by semicolons, *but* it is considered poor form.
A single statement on a line may end in a semicolon, but the
semicolon is unnecessary, and bad style.
A statement may continue across more than one line as long as the
proceeding line (1) ends in an operator or (2) is an open context
(inside "()", "[]", or "{}"), or (3) ends with a backslash as the
last character.
Names and tokens
------------------
Names in ruby are composed of letters, digits, underscores, and a
few initial special characters. In some cases, those special
characters determine the scope of the variable, as described in this
table:
======================= ===================================================
Initial character Variable Scope
======================= ===================================================
$ A global variable
[a-z] or _ A local variable
@ An instance variable
@@ A class variable (also global within a module)
[A-Z] A constant
======================= ===================================================
This table can be found at:
http://www.techotopia.com/index.php/Ruby_Variable_Scope
Predefined Ruby global variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
============== ============================================================
Variable Name Variable Value
============== ============================================================
$@ The location of latest error
$_ The string last read by gets
$. The line number last read by interpreter
$& The string last matched by regexp
$~ The last regexp match, as an array of subexpressions
$n The nth subexpression in the last match (same as $~[n])
$= The case-insensitivity flag
$/ The input record separator
$\ The output record separator
$0 The name of the ruby script file currently executing
$* The command line arguments used to invoke the script
$$ The Ruby interpreter's process ID
$? The exit status of last executed child process
$stdin The standard input file
$stdout The standard output file
$stderr The standard error output file
============== ============================================================
This table can be found at:
http://www.techotopia.com/index.php/Ruby_Variable_Scope
A few more variables that are automatically available in your
scripts:
- The magic variable ``__FILE__``, which contains the name of the
current file.
- The variable ``ARGV``, which is a synonym for ``$*``.
Also see a more extensive list of pre-defined variables at:
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Pre-defined_Variables
Blocks and indentation
------------------------
The Ruby convention for indentation is 2 spaces per indentation
level. Do *not* use hard tabs.
Ruby code blocks are delimited by keywords such as ``if
end``, ``do end``, etc. Curley brackets (``{`` and ``}``)
can also be used to surround a block in some situations.
Comments
----------
Everything to the right of a "#" character on any line is a comment.
Block comments -- You can use Ruby's ``=begin`` and ``=end`` comment
markers. For example::
=begin
This comment spans
several lines.
=end
Program structure
-------------------
- Files
- Modules
- Classes
- Functions
- Structured statements (``if``, ``while``, etc) and blocks. A
block is surrounded by ``do``-``end`` or by curly brackets.
Structured statements contain nested blocks; they begin with a
keyword (for example, ``if``, ``while``, ``until``, ``case``,
etc.) and end with the keyword ``end``. Ruby does have a ``for``
statement, *but* you will usually use and iterator instead (one of
the ``each_xxx`` methods.
Operators
-----------
You can find a summary of Ruby's operators here:
`Ruby operators`_.
And, the precedence of Ruby operators can be found here:
`Ruby operator precedence`_.
.. _`Ruby operators`: http://www.techotopia.com/index.php/Ruby_Operators
.. _`Ruby operator precedence`:
http://www.techotopia.com/index.php/Ruby_Operator_Precedence
Note that the meaning of an individual operator may be different
depending on the objects it is applied to.
Code evaluation
-----------------
When Ruby evaluates the code in a script file, it evaluates that
code from the top of the file down to the bottom. Therefore, the
order of code in that file may affect the behavior of the script.
In particular, functions must be defined before they are called.
Simple script structure
-------------------------
Here is a simple script that can be run from the command line::
def main
puts "hello"
end
main
Notes:
- The above script defines one method (``main``), then calls that
function.
Here is another, slightly less simple script that can be run from
the command line *or* can be used in another script::
#!/usr/bin/env ruby
# simple02.rb
def print_upcase_message msg
puts msg.upcase
end
def main
print_upcase_message "aBcDeFgH"
end
if __FILE__ == $0
main
end
Notes:
- The above script defines two methods: ``print_upcase_message`` and
``main``.
- When run from the command line, the function main is called.
- When used in another script via the ``require``, ``main`` is *not*
called (until the script that uses it does so explicitly).
Here is an example of the use of this script::
>> require './simple02.rb'
==>true
>> print_upcase_message 'aaaBBBccc'
AAABBBCCC
==>nil
Built-in data-types
=====================
There are *no* builtin datatypes in Ruby. Every data object is an
instance of some class. So, for information on the equivalant of
some of Python's builtin datatypes, see the documentation on the
appropriate class, e.g. String, Integer, Array, File, etc. at:
`Ruby-Doc.org --
http://ruby-doc.org/
`_. Many of these datatypes are in the Ruby
core, which means that you do not need to use the ``require``
command in order to use them.
Also, in order to get more information on a class or one of its
methods, do at the command line::
$ ri String
$ ri "String.scan"
etc.
And, inside the ruby interactive prompt, ``irb``, you can get a list
of methods available for an object by typing::
>> some_obj.methods.sort
Numeric types
---------------
See these classes:
- `Integer --
http://ruby-doc.org/core-2.0/Integer.html
`_
- `Float --
http://ruby-doc.org/core-2.0/Float.html
`_
- `Fixnum --
http://ruby-doc.org/core-2.0/Fixnum.html
`_
Ruby numeric operators will automatically coerce integers to floats
when you use mixed arithmetic. Example::
>> 3 / 6
==>0
>> 3 / 6.0
==>0.5
You can also explicitly convert an integer (or Fixnum) to a float.
Example::
>> a = 5
==>5
>> b = 3
==>3
>> a / b
==>1
>> a.to_f / b
==>1.6666666666666667
Symbols (atoms)
-----------------
Symbols are names in Ruby. They are called atoms in some other
languages. Create a symbol using the colon for a literal
representation. Symbols are singleton objects, which means that you
can test to determine whether two symbols are the same symbol using
an identity test (``.equal?``). You can also create them from
strings. Examples::
>> a = :aaa
==>:aaa
>> b = :aaa
==>:aaa
>> a.equal? b
==>true
>> a.object_id
==>430088
>> b.object_id
==>430088
You can also create symbols from strings. Example::
>> c = 'snowpea'
==>"snowpea"
>> d = c.to_sym
==>:snowpea
>> d
==>:snowpea
>> d.class
==>Symbol
>> 'oak tree'.to_sym
==>:"oak tree"
Arrays
--------
Information on class Array:
- `Array --
http://ruby-doc.org/core-2.0/Array.html
`_
Use square brackets and commas to give a literal representation of
an Array and to create it. Use the ``length`` method to get the
number of items in an Array. Example::
>> a = [11, 22, 33]
==>[11, 22, 33]
>> a.class
==>Array
>> a.length
==>3
Iteration -- Array.each etc
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To iterate over the items in an Array, use one of the ``.each``
methods:
- each
- each_cons
- each_entry
- each_index
- each_slice
- each_with_index
- each_with_object
Example::
>> a1 = [111, 222, 333]
==>[111, 222, 333]
>> a1.each { |x| puts "item: #{x}" }
item: 111
item: 222
item: 333
==>[111, 222, 333]
>> a1.each_index { |idx, x| puts "#{idx}. item: #{x}" }
0. item:
1. item:
2. item:
==>[111, 222, 333]
>> a1.each_with_index { |x, idx| puts "#{idx}. item: #{x}" }
0. item: 111
1. item: 222
2. item: 333
==>[111, 222, 333]
Index and select an item in an Array with square brackets and an
integer. Negative numbers index from the right-hand side of the
array. Example::
>> a = (1 .. 9).to_a
==>[1, 2, 3, 4, 5, 6, 7, 8, 9]
>> a[0]
==>1
>> a[2]
==>3
>> a[-1]
==>9
>> a[-2]
==>8
Slice notation
~~~~~~~~~~~~~~~~
Ruby has a "slice" notation:
- ``a[n, m]`` produces an Array of m elements starting at a[n].
- ``a[n .. m]`` produces an Array of the elements from a[n] to a[m],
including a[m].
- ``a[n ... m]`` produces an Array of the elements from a[n] to a[m],
excluding a[m].
Example::
>> a = (1 .. 9).to_a
==>[1, 2, 3, 4, 5, 6, 7, 8, 9]
>> a[2, 3]
==>[3, 4, 5]
>> a[2 .. 6]
==>[3, 4, 5, 6, 7]
>> a[2 ... 6]
==>[3, 4, 5, 6]
Test for membership in an ``Array`` with the ``.includes?`` method.
Example::
>> a = [11, 22, 33]
==>[11, 22, 33]
>> a.include? 22
==>true
>> a.include? 44
==>false
In order to use an ``Array`` as a stack, use its ``push`` and
``pop`` methods. Example::
>> pile = []
==>[]
>> pile.push 'apple'
==>["apple"]
>> pile.push 'banana'
==>["apple", "banana"]
>> pile
==>["apple", "banana"]
>> pile.pop
==>"banana"
>> pile
==>["apple"]
The ``.push`` method can take multiple arguments.
Instead of ``.push``, you can use the ``<<`` operator. Because the
``<<`` operator returns the array itself, it can be chained.
Example::
>> container = [:peach, :nectarine]
==>[:peach, :nectarine]
>> container << :apricot << :tangerine << :cherry
==>[:peach, :nectarine, :apricot, :tangerine, :cherry]
>> container
==>[:peach, :nectarine, :apricot, :tangerine, :cherry]
And, you can add the contents of an Array to another Array with the
``+=`` operator. Example::
>> a = [:aa, :bb]
==>[:aa, :bb]
>> a += [:cc, :dd]
==>[:aa, :bb, :cc, :dd]
>> a
==>[:aa, :bb, :cc, :dd]
map, select, reject, reduce, etc
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ruby also has some higher order functions that work on Arrays and on
Enumerators, also. These include the following and their synonyms:
- map and collect
- select and find_all
- reject
- map and inject
Here are a few of them:
- ``an_array.collect { |item| block }`` -- Return an Array containing
each object returned by block. Example::
>> a = [1, 2, 3, 4, 5, 6]
==>[1, 2, 3, 4, 5, 6]
>> a.collect { |item| item * 3 }
==>[3, 6, 9, 12, 15, 18]
- ``an_array.select { |item| block }`` -- Return an Array containing
each item in ``an_array`` for which block returns true. Example::
>> a = [1, 2, 3, 4, 5, 6]
==>[1, 2, 3, 4, 5, 6]
>> a.select { |item| item % 2 == 0 }
==>[2, 4, 6]
- ``an_array.reject { |item| block }`` -- Return an Array containing
each item in ``an_array`` for which block returns false.
Example::
>> a = [1, 2, 3, 4, 5, 6]
==>[1, 2, 3, 4, 5, 6]
>> a.reject { |item| item % 2 == 0 }
==>[1, 3, 5]
- ``an_array.reduce(initial) { |accum, item| block }`` -- Return the
result returned by block after applying it to ``accum`` and each
``item`` in the array. Example::
>> a = [1, 2, 3, 4, 5, 6]
==>[1, 2, 3, 4, 5, 6]
>> a.reduce([]) { |accum, item| if item % 2 == 0 then accum.push item * 3 end; accum }
==>[6, 12, 18]
Note that "inject" is another name for "reduce". Also note that
there are forms of ``reduce`` without the arguments and without
the block; see the Ruby documentation for more on that.
- ``an_array.zip(other_array1, other_array2, ...)`` -- Return an
Array of Arrays. The first item contains the first item from each
initial arrays; the second item contains the second items; etc.
Example::
>> a = [1, 2, 3]
==>[1, 2, 3]
>> b = [11, 22, 33]
==>[11, 22, 33]
>> c = [111, 222, 333]
==>[111, 222, 333]
>> a.zip(b, c)
==>[[1, 11, 111], [2, 22, 222], [3, 33, 333]]
Strings
---------
- `String --
http://ruby-doc.org/core-2.0/String.html
`_
Mutable -- Strings are mutable; they can be modified in place. Use
``my_string.methods.sort`` in irb, then look for methods that end
with a "!".
But, any individual string can be frozen so that it cannot be
modified. For example::
>> desc = "A cute cat"
==>"A cute cat"
>> desc
==>"A cute cat"
>> # Change a slice (using a range) of the string.
>> desc[2..5] = "big"
==>"big"
>> desc
==>"A big cat"
>> # Make the string immutable.
>> desc.freeze
==>"A big cat"
>> # Prove that we cannot modify a frozen string.
>> desc[2..5] = "fast"
RuntimeError: can't modify frozen String
from (irb):47:in `[]='
from (irb):47
from /usr/bin/irb:12:in `'
Multi-byte characters
~~~~~~~~~~~~~~~~~~~~~~~
Ruby strings can contain multibyte characters. Here is an example::
city = "Sel\u00e7uk"
==>"Sel\u00E7uk"
puts city
Selçuk
==>nil
city.each_char { |x| puts x }
S
e
l
ç
u
k
==>"Sel\u00E7uk"
Strings are represented internally as a sequence of characters, not
bytes. Those characters can contain unicode characters. Example::
>> puts "a\u011fb"
ağb
==>nil
>> puts "a\u011fb".length
3
==>nil
>> puts "a\u011fb"[0]
a
==>nil
>> puts "a\u011fb"[1]
ğ
==>nil
>> puts "a\u011fb"[2]
b
==>nil
Multibyte files -- For help with processing files that contain
multibyte characters, see section `Multibyte encodings`_ in this
document.
String literals
~~~~~~~~~~~~~~~~~
Double quotes and single quotes can be used to define strings.
Interpolation and backslash character escapes can be used within
double quotes. Interpolation and backslash character escapes are
treated literally inside single quotes. Example::
>> x = 3
==>3
>> 'x: #{x}'
==>"x: \#{x}"
>> "x: #{x}"
==>"x: 3"
>> sprintf 'x: %s', x
==>"x: 3"
>> '\t'
==>"\\t"
>> '\t'.length
==>2
>> "\t"
==>"\t"
>> "\t".length
==>1
Bachslash escape sequences
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a table that describes them:
========= ============================================================
Escape Sequence Meaning
========= ============================================================
\\n newline (0x0a)
\\s space (0x20)
\\r carriage return (0x0d)
\\t tab (0x09)
\\v vertical tab (0x0b)
\\f formfeed (0x0c)
\\b backspace (0x08)
\\a bell/alert (0x07)
\\e escape (0x1b)
\\nnn character with octal value nnn
\\xnn character with hexadecimal value nn
\\unnnn Unicode code point U+nnnn (Ruby 1.9 and later)
\\cx control-x
\\C-x control-x
\\M-x meta-x
\\M-\\C-x meta-control-x
\\x character x itself (\\" a quote character, for example)
========= ============================================================
Interpolation
^^^^^^^^^^^^^^^
Use "#{expression}" within a double quoted string,
where ``expression`` can be almost any ruby code. Example::
>> size = 35
==>35
>> description = "pretty"
==>"pretty"
>> "double size: #{size * 2}"
==>"double size: 70"
>> "upper description: #{description.upcase}"
==>"upper description: PRETTY"
The % notation
^^^^^^^^^^^^^^^^
Use % followed by a non-alpha-numeric character.
This notation is especially useful when you want to include double
quote and single quote characters in your string.
Brackets are especially handy. Brackets can be contained within
brackets if they balance. Example::
>> %[this contains "quote" characters, doesn't it]
==>"this contains \"quote\" characters, doesn't it"
>> %[this on has [embedded] brackets]
==>"this on has [embedded] brackets"
Modifiers for the % notation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a table of possible modifiers:
========= ==================================================================
Modifier Meaning
========= ==================================================================
%q[ ] Non-interpolated String (except for \\ \[ and \])
%Q[ ] Interpolated String (default)
%r[ ] Interpolated Regexp (flags can appear after the closing delimiter)
%i[ ] Non-interpolated Symbol (after Ruby 2.0)
%I[ ] Interpolated Symbol (after Ruby 2.0)
%w[ ] Non-interpolated Array of words, separated by whitespace
%W[ ] Interpolated Array of words, separated by whitespace
%x[ ] Interpolated shell command
========= ==================================================================
The "here document" notation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use "<> long_msg = <"This is a long message of\nfluff fluff fluff fluff\nfluff fluff fluff fluff\nfluff fluff fluff fluff\nfluff fluff fluff fluff\n"
>> puts long_msg
This is a long message of
fluff fluff fluff fluff
fluff fluff fluff fluff
fluff fluff fluff fluff
fluff fluff fluff fluff
==>nil
Interpolation is performed inside a "here document" string, except
that if you use single quotes around the delimiter, it's not.
And, you can "stack" multiple here documents::
string = [< ["the first thing\n", "the second thing\n", "and the third thing\n"]
This example came from the Ruby Wiki. See that document for more on
string literals:
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#Strings
sprintf string formatting and the % operator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``sprintf`` provides capabilities similar to string interpolation
with ``#{expr}``, but with more control over formatting.
The ``%`` operator can be used instead of ``sprintf``. For example::
>> a = 5
==>5
>> b = 'abcd'
==>"abcd"
>> puts "%03d -- %s" % [a, b]
005 -- abcd
==>nil
See the docs in the ``Kernel`` module:
http://ruby-doc.org/core-2.0/Kernel.html#method-i-sprintf
In particular, the formatting flags, type codes, width, and
precision specification are described there.
Here are several examples::
>> sprintf("%f %d", 1.23, 54)
==>"1.230000 54"
>> sprintf "letter A: %x %d", 'A'.ord, 'A'.ord
==>"letter A: 41 65"
>> sprintf "%.4f %g", 1.23, 300000
==>"1.2300 300000"
>> sprintf "%.4f %g", 1.23, 30000000000
==>"1.2300 3e+10"
>> "abcd".ljust 20
==>"abcd "
>> "abcd".rjust 20
==>" abcd"
>> "abcd".center 20
==>" abcd "
>> sprintf "%20s", "abcd" # right justify
==>" abcd"
>> sprintf "%-20s", "abcd" # left justify
==>"abcd "
String concatenation
~~~~~~~~~~~~~~~~~~~~~~
You can use ``+``, ``<<``, and ``.concat`` to concatenate strings. The
``<<`` and ``.concat`` operations modify a string in place, and are
therefore faster than ``+`` when used to accumulate large strings.
But, use string interpolation (``"...#{expr}..."``), rather than
concatenation, when formatting a string in a single operation.
Converting strings to arrays and arrays to strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To convert an Array of strings into a single string, use the Array's
``.join`` method. Example::
>> a = ['the', 'bright', 'blue', 'balloon']
==>["the", "bright", "blue", "balloon"]
>> a.join
==>"thebrightblueballoon"
>> a.join ' '
==>"the bright blue balloon"
>> a.join '||'
==>"the||bright||blue||balloon"
To convert an Array of some other kind of objects into a single
string, consider using the ``Array.collect`` method. For example::
>> b = [11, 22, 33, 44]
==>[11, 22, 33, 44]
>> c = b.collect {|x| x.to_s }
==>["11", "22", "33", "44"]
>> c.join "||"
==>"11||22||33||44"
Although, the ``Array.join`` method seems to do some conversion of
items to strings automatically, and even seems to handle Arrays
nested inside Arrays.
To convert a String into an Array, use the String's ``.split``
method. Example::
>> b = "the bright blue balloon"
==>"the bright blue balloon"
>> b.split
==>["the", "bright", "blue", "balloon"]
>> b.split(/\W/)
==>["the", "bright", "blue", "balloon"]
>> b = "the bright\tblue balloon"
==>"the bright\tblue balloon"
>> b.split
==>["the", "bright", "blue", "balloon"]
>> b = 'abcd'
==>"abcd"
>> b.split(//)
==>["a", "b", "c", "d"]
>>
>> # Use a regular expression to split on punctuation as well
>> # as white space.
>> data = "aaa bbb, ccc. \"ddd eee\" fff"
==>"aaa bbb, ccc. \"ddd eee\" fff"
>> data.split /\W+/
==>["aaa", "bbb", "ccc", "ddd", "eee", "fff"]
Notes:
- The default delimiter (separator) is white space, when no pattern
is given.
- You can use a regular expression to split on more complicated
patterns. For example, "/\\W+/" will split off punctuation as
well as white space. See example above.
- Use the empty pattern (``//``) in order to split a string into
individual characters. The pattern ``//`` matches between each
character. For more on regular expressions, see:
http://www.ruby-doc.org/core-2.1.2/Regexp.html. Or, if you have
Ruby doc support, do ``$ ri regexp``.
- But remember that you can process the characters in a String using
its ``.each_char`` method and a block. So, in many cases, it will
not be necessary to convert the String to an Array. Example::
>> b = 'abcd'
==>"abcd"
>> b.each_char { |ch| puts "ch: #{ch}" }
ch: a
ch: b
ch: c
ch: d
==>"abcd"
Hash -- dictionary
--------------------
In Ruby, a dictionary is called a Hash. See:
- `Hash --
http://ruby-doc.org/core-2.0/Hash.html
`_
Here is the literal syntax for a hash::
>> table = {"peach" => "tasty", "necartine" => "tangy"}
==>{"peach"=>"tasty", "necartine"=>"tangy"}
Or, if the keys are atoms, you can use this alternative literal
syntax::
>> table = {peach: "tasty", nectarine: "tangy"}
==>{:peach=>"tasty", :nectarine=>"tangy"}
You can mix these two syntaxes in the same literal expression::
>> table = {peach: "tasty", nectarine: "tangy", "tree" => "branchy"}
==>{:peach=>"tasty", :nectarine=>"tangy", "tree"=>"branchy"}
>> table
==>{:peach=>"tasty", :nectarine=>"tangy", "tree"=>"branchy"}
Then use square brackets to add, replace, and retrieve items::
>> # Add an item.
>> table[:apricot] = "sweet"
==>"sweet"
>> table
==>{:peach=>"tasty", :nectarine=>"tangy", :apricot=>"sweet"}
>> # Replace an item.
>> table[:peach] = "succulent"
==>"succulent"
>> # Retrieve a value from the table.
>> puts table[:nectarine]
tangy
==>nil
Remove a key/value pair from a hash -- Example::
>> table
==>{:peach=>"tasty", :nectarine=>"tangy", :banana=>"rich"}
>> table.delete(:banana)
==>"rich"
>> table
==>{:peach=>"tasty", :nectarine=>"tangy"}
Check to determine whether a key is in the hash, use ``has_key?``,
``include?``, or ``member?``. They are all synonyms::
>> table
==>{:peach=>"tasty", :nectarine=>"tangy"}
>> table.has_key? :banana
==>false
>> table.has_key? :peach
==>true
What can I use as a key in a Hash? -- You can use any object that
responds to the method ``.hash``. However, ...
*Caution:* If you add a value to a Hash using a mutable object (for
example an Array) as the key in a hash and then you modifiy that
object (for example, add an item to the Array), in some cases that
mutable object can no longer be used to successfully access that
value. Suggestion: if you intend to use an array as a key in a
hash, consider using ``myarray.freeze`` to make that
array immutable.
You can also set a default value to be returned when you as for the
value of a key that does not exist in the dictionary. If you do not
explicitly set the default, then it is ``nil``. In other words, the
default for the default is nil. Example::
>> table = Hash.new('nothing')
==>{}
>> p table[:abc]
"nothing"
==>"nothing"
>> table.default = 'something'
==>"something"
>> p table[:abc]
"something"
==>"something"
Files
-------
Use class ``File``. See:
- `File --
http://ruby-doc.org/core-2.0/File.html
`_
``File.open`` opens a file for read, write, append, etc access and
creates a ``File`` object. Once you have opened a file (and created
a File object), you can learn about it by typing the following at
the ``irb`` interactive prompt::
> my_file = File.open('some_file.txt', "r")
> my_file.methods.sort
And, here is a piece of code that reads a text file and writes out
each line capitalized::
def read_file(infilename)
infile = File.open(infilename, "r")
infile.each do |line|
puts line.upcase
end
infile.close
end
read_file("test.txt")
Notes:
- A file object has a number of ".each" methods. You can learn
about them with ``ri``. Or look here:
- http://www.ruby-doc.org/core-2.0.0/File.html
- http://www.ruby-doc.org/core-2.0.0/IO.html#method-i-each
- In particular, there is an ``file_obj.each_with_index`` that gives
an index number with each line.
- Don't forget to close the file when you are finished, for example:
``file_obj.close``.
And, the following example writes several lines to a text file::
def write_file outfilename
data = [
"line 1",
"line 2",
"line 3",
"line 4",
]
outfile = File.open outfilename, "w"
data.each_with_index {|item, idx|
outfile.write "#{idx + 1}. #{item}\n"
}
outfile.close
end
write_file "tmp01.txt"
Notes:
- We need to explicitly add the new line character ("\\n") to the
end of each line.
- The method ``Array.each_with_index`` gives us an iterator that
provides both the next item in the ``Array`` and an index
(starting at 0) for each iteration.
Standard out and standard err
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can write to ``stdout`` and ``stderr`` as follows::
$stdout.puts "This is a routine message"
$stderr.puts "This is a error message"
Standard input
~~~~~~~~~~~~~~~~
You can read the input that was "piped" to your program/script by
using file ``$stdin``. Look for the various ``.read`` and ``.each``
methods.
Here is a sample script that reads standard input (``$stdin``),
converts text to upper case, and writes to standard output
(``$stdout``)::
def filter
lines = $stdin.readlines
lines.each do |line|
line = line.upcase
$stdout.write line
end
end
filter
If the above script is in a file ``filter.rb``, then you can run it
as follows::
$ cat filter.rb | ruby filter.rb
DEF FILTER
LINES = $STDIN.READLINES
LINES.EACH DO |LINE|
LINE = LINE.UPCASE
$STDOUT.WRITE LINE
END
END
FILTER
Multibyte encodings
~~~~~~~~~~~~~~~~~~~~~~
You will sometimes need to process files containing multibyte
encodings. To do so, specify the encoding when you open the file.
For example::
f = open("testdata01.txt", "r:utf-8")
==>#
f.each { |line| puts "line: #{line}" }
line: aaa
line: bbb
line: çöğ
line: ccc
==>#
f.close
==>nil
To process individual characters (as opposed to bytes) look for
methods ``.chars``, ``.each_chars``, etc in data read from the file
or in the file object itself. Also note that ``my_str[n]`` indexes
and selects *characters*, not *bytes*, and that ``my_str.length``
returns the number of *characters* in the string, not the number of
*bytes*.
Regular expressions
---------------------
Define regular expressions with a pattern inside forward slashes.
Example::
>> pattern1 = /x*(abc)y*(def)z*/
If forward slashes are not a convenient delimiter (for examle,
because your pattern contains forward slashes, then you can create a
regular expression as follows::
>> pattern2 = Regexp.new("x*(abc)y*(def)z*")
Use a regular expression with any of these operators:
- ``=~`` -- Returns the position of a match within the target if
sucessful, else ``nil``. Use it to determine whether or not there is
a successful match (``nil`` indicates failure) and to determine
the location of the match in the target. Example::
>> "xxabbcyyydeeefzzzz" =~ /x*(ab*c)y*(de*f)z*/
==>0
>> $&
==>"xxabbcyyydeeefzzzz"
>> $~
==>#
>> $1
==>"abbc"
>> $2
==>"deeef"
>> "aaabbbcccdddeee" =~ /ccc/
==>6
Note the use of the special variables ``$&``, ``$~``, ``$1``,
``$2``, etc. to obtain the results of the last match. But, a
better way is to use ``target.match(regexp)`` or
``regexp.match(target)``, which return a ``MatchData`` object.
- ``regexp_pattern.match(target_string)`` -- Similar to the use of
``=~``, except that the return value is a ``MatchData`` object or
``nil`` if the match is not successful. Use it to extract the
characters from the target that are matched by groups in the
pattern. (Groups are surrounded by parentheses in the
``Regexp``.) Example::
>> /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz")
==>#
>> /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz").captures
==>["abbc", "deeef"]
>> /x*(ab*c)y*(de*f)z*/.match("123")
==>nil
>> md = "aaa111bbb222ccc".match(/[^\d]*(\d+)[^\d]*(\d+)/)
==>#
>> md.captures
==>["111", "222"]
- ``!~`` -- Returns ``false`` if the match is successful, else
``true``. As a memory aid, remember that ``!=`` reverses the
sense of ``==``. Example::
>> "111222333" !~ /222/
==>false
>> $&
==>"222"
>> $~
==>#
>> "111222333" !~ /555/
==>true
>> $&
==>nil
``MatchData`` objects -- Here are some things you can do with the
``MatchData`` objects that are returned by
``regexp_pattern.match(target_string)`` and that is the value of the
``$~`` special variable after a successful match::
>> mdobj = /x*(ab*c)y*(de*f)z*/.match("xxabbcyyydeeefzzzz")
==>#
>> mdobj
==>#
>> mdobj.class
==>MatchData
>> mdobj.length
==>3
>> mdobj[0]
==>"xxabbcyyydeeefzzzz"
>> mdobj[1]
==>"abbc"
>> mdobj[2]
==>"deeef"
>> mdobj.regexp
==>/x*(ab*c)y*(de*f)z*/
There are other objects that have methods that take a ``Regexp`` as
an argument. ``String.match`` and ``String.scan`` are two useful
ones.
Here is a pattern that you can use to search a string for repeating
occurances of a pattern and process each one::
$target1 = "111aabb222aaabbb333aaaabbbb444"
$pat1 = /\d(a+)(b+)\d/
def test
puts "pattern: #{$pat1}"
puts "target: #{$target1}"
pos = 0
while md = $target1.match($pat1, pos)
puts "#{pos} #{md[0]} #{md[1]} #{md[2]}"
pos = md.end(0)
end
puts "last pos: #{pos}"
end
test
When run, this script displays the following::
$ ruby regexp_example.rb
pattern: (?-mix:\d(a+)(b+)\d)
target: 111aabb222aaabbb333aaaabbbb444
0 1aabb2 aa bb
8 2aaabbb3 aaa bbb
17 3aaaabbbb4 aaaa bbbb
last pos: 28
Other built-in types
----------------------
Symbols
~~~~~~~
Symbols are marked with a leading colon, for example: ``:apple``,
``:red``, ``:monday``.
Symbols stand for themselves. They are singletons. Two symbols that
are spelled the same are guarantteed to be identical: they have the
same object ID (returned by ``.object_id``).
You can test for the identity of two symbols (whether they are the
same symbol) with ``.equal?``. Example::
>> s1 = :tomato
==>:tomato
>> s2 = :eggplant
==>:eggplant
>> s3 = :tomato
==>:tomato
>> s1.equal? s2
==>false
>> s1.equal? s3
==>true
Notes:
- Symbols are often a good choice for use as keys in a Hash
(dictionary).
- Symbols are *not* garbage collected, whereas strings are. So, if
your program needs a huge amount of symbols, consider the memory
impact.
The nil/None value/type
~~~~~~~~~~~~~~~~~~~~~~~~~
In Ruby, "None" is called "nil". It is an instance of class
``NilClass``.
You can test for ``nil`` with either of the following::
> my_obj.nil?
> not my_obj.nil?
Boolean values
~~~~~~~~~~~~~~~~
Ruby's boolean values are ``true`` and ``false``.
In the condition of Ruby statements like ``if``, ``while``,
``until``, etc, the values ``false`` and ``nil`` count as false;
everything else is true.
Ranges
~~~~~~~~
A range is an instance of class ``Range``.
Ranges are defined with ``n .. m`` or ``n ... m`` (m >= n):
- ``..`` includes the last item (m).
- ``...`` excludes the last item (m).
The end points n and m can be integers, floats, characters, strings,
... The end point objects must be comparable with ``<=>``.
With *some* ranges, you can use ``range.to_a`` to produce an
``Array`` from a ``Range``.
Test whether an object is "in" a ``Range`` with ``range.include?
obj``.
Use methods in the ``.each`` family to iterate over elements in a
range.
You can also create ranges with ``Range.new(start, end,
exclusive=false)``.
Example::
>> range1 = 2 .. 4
==>2..4
>> range2 = 2 ... 4
==>2...4
>> range1.to_a
==>[2, 3, 4]
>> range2.to_a
==>[2, 3]
>> range1.each { |item| puts "item: #{item}" }
item: 2
item: 3
item: 4
==>2..4
>> range3 = 4.5 .. 8.6
==>4.5..8.6
>> range3.include? 7.1
==>true
>> range3.include? 10.8
==>false
Sets and frozensets
~~~~~~~~~~~~~~~~~~~~~
Sets are defined in a separate module. Use ``require`` to make it
available. Here is an example::
>> require 'set'
==>true
>>
>> # Create a set of atoms.
>> basket1 = Set.new [:apple, :banana, :nectarine, :peach, :watermelon]
==>#
>> # Add an item to the set.
>> basket1.add :cantaloupe
==>#
>> basket2 = Set.new [:apple, :banana]
==>#
>> # Test whether a set contains an atom.
>> basket2.include? :nectarine
==>false
>> basket1.include? :nectarine
==>true
>> basket2.add :apricot
==>#
>> # Get the union of two sets.
>> basket1.union basket2
==>#
>> # Check to see if basket1 was modified.
>> basket1
==>#
There is no separate frozen set class, but, you can freeze a set.
Example::
>> require 'set'
==>true
>> a = Set.new()
==>#
>> a.add(:apricot)
==>#
>> a.freeze
==>#
>> a.add(:boysenberry)
RuntimeError: can't modify frozen Hash
from /usr/lib/ruby/1.9.1/set.rb:228:in `[]='
from /usr/lib/ruby/1.9.1/set.rb:228:in `add'
from (irb):80
from /usr/bin/irb:12:in `'
Functions and Classes -- A Preview
====================================
Define a function with ``def``/``end``. Parameters are optional.
Use ``return`` to return a value, or list the value as the last
value produced. For example, we can define a function in a file
named ``tmp.rb``::
def greater(a, b)
if a < b
return false
elsif a > b
return true
end
false
end
And, then we use it in ``irb`` as follows::
>> load './tmp.rb'
==>true
>> greater(3, 2)
==>true
>> greater(4,4)
==>false
>> greater(4,6)
==>false
Statements
============
Assignment
------------
Ruby's assignment statement is very much like Python's.
You can even do unpacking. For example::
>> a = ["abc", 123]
==>["abc", 123]
>> b, c = a
==>["abc", 123]
>> b
==>"abc"
>> c
==>123
Ruby has "augmented" assignment operators:
============= ===================
x += y x = x + y
x -= y x = x - y
x /= y x = x / y
x \*= y x = x \* y
x %= y x = x % y
x \*\*= y x = x \*\* y
============= ===================
Ruby can do parallel assignment and can swap values. Example::
>> # Parallel assignment.
>> a, b, c = :apricot, :banana, :cherry
==>[:apricot, :banana, :cherry]
>> puts a, b, c
apricot
banana
cherry
==>nil
>> # Swap the values in a and b.
>> a, b = b, a
==>[:banana, :apricot]
>> puts a, b, c
banana
apricot
cherry
==>nil
Note that the values on the right hand side (the Rvalue) are
evaluated completely *before* the variables on the left hand side
(the Lvalue) are modified.
require and require_relative
------------------------------
Load external modules that you want to use in your Ruby script with
the ``require`` method. (``require`` is a method in the ``Kernel``
module). Example -- Suppose file ``node_lib.rb`` contains this::
#!/usr/bin/env ruby
module NodeLib
class Node
o
o
o
Then you can load it and use the ``Node`` class in it as follows::
require "node_lib"
node1 = NodeLib::Node.new(...)
Notes:
- Reference items in a module using the module name, for example:
``ModuleName::Item``.
- Items define outside of a ``module`` block become global in the
script that uses ``require``.
- ``load`` works like ``require``, except that ``load`` forces the
module to be loaded even if it has been previously loaded.
- The list of directories to be searched for modules is in global
variable ``$:``. You can add additional directories with
something like this::
>> $:.push "."
>> $:.push "/usr/home/myself/ruby_modules"
Although, that is a questionable thing to do.
- Better, in most situations, is to add the directory containing the
code you want to reference with ``require`` to the ``RUBYLIB``
environment variable. See the section on `Modules`_ elsewhere in
this document.
- Use ``require_relative`` instead of ``require`` to ask Ruby to
search for modules in directories relative to the location where
execution is taking place.
- ``require`` evaluates the code in a file. So if you have code in
that file which you do not want to be evaluated when loaded with
``require``, but you do want evaluated when the script is run,
then hide that code inside this::
if __FILE__ == $0
# Code to be run when script is executed but not when "required".
o
o
o
end
puts and print
----------------
``puts`` and ``print`` are not actually statements. They are
functions in the ``Kernel`` module.
Use ``puts`` to display information. It's a method in ``Kernel``.
``puts`` automatically adds a newline. If you do not want the
newline added, then use ``$stdout.write``. Example::
$stdout.write "You say goodbye"
$stdout.write "I say hello"
Or, use ``print`` to write out text without a newline automatically
added::
>> a = 3; puts "a: "; puts a
a:
3
==>nil
>> a = 3; print "a: "; puts a
a: 3
==>nil
if elif else
-----------------
Here is a sample ``if`` statement::
def greater(a, b)
if a < b
return false
elsif a > b
return true
else
return false
end
end
puts "3 < 4: #{greater(3, 4)}"
puts "4 < 3: #{greater(4, 3)}"
puts "4 < 4: #{greater(4, 4)}"
Running this file (if_statement.rb) produces the following::
$ ruby tmp.rb
3 < 4: false
4 < 3: true
4 < 4: false
Notes:
- There can be multiple ``elsif`` clauses. They are all optional.
- The else clause is optional.
- The condition that follows the ``if`` and ``elsif`` keywords can
be any expression.
- The values ``false`` and ``nil`` count as false; every other value
counts as true.
- Add keyword ``then`` after the condition, if you want a statement
following the condition on the same line as the condition.
Example::
>> name = "Dave"
==>"Dave"
>> if name == "Dave" then puts "Hello" end
Hello
==>nil
But, usually, this is considered bad form.
- A condition can be any expression, that is anything that returns a
value.
- The logical operators are ``not``, ``and`` (or ``&&``), and ``or``
(or ``||``).
- Use parentheses to override the precedence of operators, or when
you feel that parentheses help clarify the order of evaluation.
- Parentheses can also be used to continue a long condition across
multiple lines.
unless statement
~~~~~~~~~~~~~~~~~~
An ``unless`` statement is like an ``if`` statement with the sense
of the test reversed. You can think of "unless ..." being equivalent to
"if not ...". Example::
>> flag = true
==>true
>> value = if flag then 100 else 0 end
==>100
>> value
==>100
>> flag = false
==>false
>> value = if flag then 100 else 0 end
==>0
>> value = unless flag then 100 else 0 end
==>100
>> value = if not flag then 100 else 0 end
==>100
True and false values in conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In a condition (for example, in an ``if``, ``unless``, and ``while``
statement), the values ``false`` and ``nil`` count as false; every
other value counts as true. In other words, ``false`` and ``nil``
cause a condition to fail; everything else causes it to succeed.
if expression
~~~~~~~~~~~~~~~
The ``if`` statement is itself an expression. In Ruby, statements
return a value and are, therefore, expressions.
However, when only the value is needed, you may find it more
convenient to use an ``if`` expression. The form of an ``if``
expression is as follows::
? :
Examples::
>> a = 4
==>4
>> b = 6
==>6
>> a < b ? -1 : +1
==>-1
>> b < a ? -1 : +1
==>1
if and unless modifier
~~~~~~~~~~~~~~~~~~~~~~~~
You can also use an ``if`` expression as a modifier on a statement.
Example::
>> flag = true
==>true
>> puts "hello" if flag
hello
==>nil
>> flag = false
==>false
>> puts "hello" if flag
==>nil
>> puts "hello" unless flag
hello
==>nil
Operators for conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~
You can learn about the precedence of all Ruby operators here:
`Ruby operator precedence`_.
Ruby operators (highest to lowest precedence) -- Operators with a
"Yes" in the method column are actually methods, and as such may be
overridden:
+--------+------------------------+--------------------------------------+
| Method | Operator | Description |
+========+========================+======================================+
| Yes | [ ] [ ]= | Element reference, element set |
+--------+------------------------+--------------------------------------+
| Yes | \*\* | Exponentiation (raise to the power) |
+--------+------------------------+--------------------------------------+
| | | Not, complement, unary plus and |
| Yes | ! ~ + - | minus (method names for the last two |
| | | are +@ and -@) |
+--------+------------------------+--------------------------------------+
| Yes | \* / % | Multiply, divide, and modulo |
+--------+------------------------+--------------------------------------+
| Yes | \+ \- | Addition and subtraction |
+--------+------------------------+--------------------------------------+
| Yes | >> << | Right and left bitwise shift |
+--------+------------------------+--------------------------------------+
| Yes | & | Bitwise AND |
+--------+------------------------+--------------------------------------+
| Yes | ^ | | Bitwise exclusive OR and regular |
| | | OR |
+--------+------------------------+--------------------------------------+
| Yes | <= < > >= | Comparison operators |
+--------+------------------------+--------------------------------------+
| | | Equality and pattern match operators |
| Yes | <=> == === != =~ !~ | (!= and !~ may not be defined as |
| | | methods) |
+--------+------------------------+--------------------------------------+
| | && | Logical AND |
+--------+------------------------+--------------------------------------+
| | || | Logical OR |
+--------+------------------------+--------------------------------------+
| | .. ... | Range (inclusive and exclusive) |
+--------+------------------------+--------------------------------------+
| | ? : | Ternary if-then-else |
+--------+------------------------+--------------------------------------+
| | = \%= \{ /= | Assignment |
| | -= += \|= \&= | |
| | >>= <<= | |
| | \*= &&= ||= \*\*= | |
+--------+------------------------+--------------------------------------+
| | defined? | Check if specified symbol defined |
+--------+------------------------+--------------------------------------+
| | not | Logical negation |
+--------+------------------------+--------------------------------------+
| | or and | Logical composition |
+--------+------------------------+--------------------------------------+
| | if unless while until | Expression modifiers |
+--------+------------------------+--------------------------------------+
| | begin/end | Block expression |
+------------------------------------------------------------------------+
for statement
---------------
Here is an example of a ``for`` statement::
def for_statement
container = [111, 222, 333]
for item in container do
puts "item: #{item}"
end
end
for_statement
If this code is in file ``for_statement.rb`` and you run it, you
will see::
$ ruby for_statement.rb
item: 111
item: 222
item: 333
Notes:
- Advice -- Do not use the ``for`` statement. Look for a way to use
an enumerator. Usually, this means looking for one of the methods
in the ``each`` family, e.g. ``.each``, ``.each_with_index``, etc.
For example::
>> container = [111, 222, 333]
==>[111, 222, 333]
>> container.each do |item|
>> puts "item: #{item}"
end
item: 111
item: 222
item: 333
==>[111, 222, 333]
- The ``do`` keyword in the ``for`` statement is optional unless you
want to put the statement in the block on the same line, which is
bad form except in ``irb``.
- The ``for`` statement will also do unpacking. Example::
>> for x, idx in container.each_with_index do puts "idx: #{idx} x: #{x}" end
idx: 0 x: 111
idx: 1 x: 222
idx: 2 x: 333
==>[111, 222, 333]
But, again, the above is likely better re-written as::
>> container.each_with_index do |x, idx| puts "idx: #{idx} x: #{x}" end
idx: 0 x: 111
idx: 1 x: 222
idx: 2 x: 333
==>[111, 222, 333]
- Scope:
- The variables defined and used in the block passed to an
enumerator are local to the block.
- The variables created by a ``for`` statement are local to the
enclosing block.
while statement
-----------------
The ``while`` statement repeats a block as long as a condition is
true. Example::
def test
idx = 0
while idx < 4
puts "idx: #{idx}"
idx += 1
end
end
test
If we put this in a file (e.g. ``while_statement.rb``), and run it,
we'll see::
$ ruby while_statement.rb
idx: 0
idx: 1
idx: 2
idx: 3
But ``while`` statements are often more clearly written using an
enumerator. This example is roughly equivalent to the above
``while`` statement::
>> (0 .. 3).each { |item| puts "item: #{item}" }
item: 0
item: 1
item: 2
item: 3
==>0..3
until statement
-----------------
Ruby also had an ``until`` statement, which is similar to the
``while`` statement, except: (1) the keyword ``until`` is used in
place of ``while``; and (2) the sense of the condition is reversed
(the block is evaluated while the condition is *not* true. This can
sometimes simplify writing the condition.
Example::
def test3
idx = 0
until idx > 8
idx += 1
if idx.modulo(2) == 0
next
end
puts "idx: #{idx}"
end
end
break and next
----------------
The ``break`` statement exits the immediately enclosing loop. Example::
def test2
(3 .. 10).each do |item|
if item >= 7
break
end
puts "item: #{item}"
end
end
test2
If we put this in a file and run it, we'll see::
$ ruby tmp2.rb
item: 3
item: 4
item: 5
item: 6
The ``next`` statement starts execution at the top of the loop.
Example::
def test1
idx = 0
while idx < 8
idx += 1
if idx.modulo(2) == 0
next
end
puts "idx: #{idx}"
end
end
test1
If we put this in a file and run it, we'll see::
$ ruby tmp2.rb
idx: 1
idx: 3
idx: 5
idx: 7
The ``redo`` statement starts execution at the top of the loop, but
*without* evaluating the condition or fetching the next element.
Exceptions -- begin-rescue-end
--------------------------------
Here is a sample script that:
1. Defines several exception classes. The are subclasses class
Exception.
2. Implements a function (``f1``) that calls another funtion
(``f2``) that raises an exception.
3. Calls function ``f1`` and handles the exception that might be
raised.
::
class ModerateException < Exception
end
class ExtremeException < Exception
end
def f1 value
f2 value
end
def f2 value
case value
when "bad"
raise ModerateException, "things are bad"
when "verybad"
raise ExtremeException, "things are very bad"
end
return "every thing is ok"
end
def test arg1
result = "undecided"
begin
result = f1 arg1
rescue ModerateException => msg
puts "handling moderate exception. msg: \"#{msg}\""
rescue ExtremeException => msg
puts "handling extreme exception. msg: \"#{msg}\""
end
puts "result: \"#{result}\""
end
if __FILE__ == $0
if $*.length != 1
$stderr.puts "usage: ruby exceptions.rb