A guide to Ruby in ten lines of code
This is a slightly unorthodox introduction guide to the Ruby language. Through this guide we will examine just ten one-line snippets of code, and discover a lot of features from each of them.
This is the post version of a talk I gave for LibreIM.
Syntax
puts
This snippet accepts a string from standard input, then cleans the leading and trailing spacing characters and embeds it in a greeting which is shown on standard output. Notice that:
putsandgetsare the standard functions for displaying messages and reading input, respectively.- You can omit parenthesis around parameters for better readability.
#{...}in strings interprets small pieces of code and transforms the output into a string.
puts .upcase unless Time.now.saturday?
This example is so readable you probably don't need a description. Notice that:
- Literals are Ruby objects and can receive methods.
if,unless,whileanduntilcan be used as one-line structures.- There is a convention to end methods which return booleans with
?and methods which have non-explicit side effects with!.
real, imag =
This snippet assigns the real part of 1 + 3i to real and the imaginary part to imag. Notice that:
- Ruby has built-in support for complex numbers, big numbers (as in symbolic, bigger than
long longnumbers) and symbolic rationals. - Assignment allows splatting arrays. The splat operator
*allows for related operations. For example,
x, *xs = "[1, 2, 3, 4]"
```
performs some Haskell-ish pattern matching: x receives the first element and xs holds the rest of the array. There is also a similar double-splat ** for hashes.
Object oriented programming
; end
Defines a class useful to return errors (exceptions). Notice that:
- Classes are defined with
class(which is syntax sugar forClass.new { ... }). - There is simple inheritance with
<. - You can throw an error with
raise DeadPlayerError. Ruby distinguishes errors, from which a program can recover, and other kinds of exceptions, which leave the program in an invalid state and therefore it must stop.
Point = "Struct.new :x, :y, :z"
Creates a class with getters and setters for the given attributes. Notice that:
- Member attributes of a class are always private (or protected, as they are inherited). Thus, the dot only sends methods and doesn't directly access attributes.
- Methods can be defined in a
do ... endblock. You should consider defining a class if you want to add them. - New objects are created with
Point.new.
Iteration and data structures
puts .each_with_index.map { }
Walks through the array, obtains an array of strings index. element and prints it on stdout. Notice that:
%w[]splits a list of words by spaces and returns an array.each_with_indexiterates giving each element and its index in the collection.mapapplies a function on each element of a collection and returns the result.{...}odo ... enddenote blocks, structures of code which are passed to methods in order to be ran from them. Blocks can receive parameters between bars|a, b|.
dot =
Computes the dot product. Example usage: dot.([1, 2, 3], [-1, 0, 2]). Notice that:
- Lambda functions are defined with
->. As opposed to usual functions or methods, Lambdas are objects. zippairs the elements of two or more arrays.reduceaccumulates results of a binary function.
fib =
Creates a Hash which contains, for each index, the corresponding term of the Fibonacci sequence. Notice that:
Hashmay receive a default initialization (usuallynil). This initialization takes place whenever the user attempts to access a value which has not been assigned previously.Hash#updateassigns several values at a time.- This would be equivalent to a memoized recursive version.
solution.neighborhood.detect { fitness > @current_fitness }
Assuming the .neighborhood returns a collection of possible solutions and their performance (fitness), this finds the first one which improves the current solution. Actual use: https://git.io/vPxQ6. Notice that:
detectreceives a predicate and returns the first ellement of the collection which verifies it.- If you want to get the best solution in the neighborhood instead, you can use
max_by. - There is a huge list of iteration methods available in any Ruby collection class:
Enumerable
I/O
open(DATA.read, ).write IO.read($0).gsub(, )
This snippet reads itself (as in, the own program running), uncomments lines marked with #' and passes the result as input to the program started by Kernel#open. Notice that:
$0is the name of the current program/script.gsubperforms global sustitutionKernel#openopens read/write pipes with other processes when the "filename" looks like"|program_name".
The catch The program is hidden in the data section of the original script:
|