Welcome to Ruby!
Ruby is a dynamic, object-oriented, general-purpose programming language. It is ranked among the top 10 programming languages worldwide. Much of its growth is attributed to the popularity of software written in Ruby, particularly the Ruby on Rails web framework.
A quote from its creator, Yukihiro "Matz" Matsumoto: "Ruby is simple in appearance, but is very complex inside, just like our human body."
Matsumoto has said that Ruby is designed for programmer productivity and fun, following the principles of good user interface design.
In Ruby, everything (even a simple number) is an object. We will learn more about objects in the coming lessons. Ruby is also completely free. Not only free of charge, but also free to use, copy, modify, and distribute. ------------------------------------------------------------------------------------------- Hello, Ruby!
Let's create our very first Ruby program, the classic "Hello World" program. For this, we use the built-in puts method. puts "Hello World"
This code will output the text "Hello World" to the screen. All text values (strings) must be enclosed in single or double quotes. -------------------------------------------------------------------------------------------- Hello, Ruby!
Another method that can be used to display output to the screen is print. For example: print "Hello World"
This code displays the same output as before, except that the puts automatically adds a new line after the output, while print does not. For example: puts "Hi" print "there" print "Ruby"
Result: Hi thereRuby -------------------------------------------------------------------------------------------- Comments
Comments are lines of annotation within Ruby code that are ignored at program runtime. In Ruby, the hashtag symbol is used to create a single-line comment. For example: # printing some text puts "Hi there"
Everything following the hashtag symbol on that line is ignored when the program is run. --------------------------------------------------------------------------------------------- Comments
You can also create multi-line comments. Everything between the =begin and =end reserved words are considered a comment: =begin This comment spans multiple lines =end puts "Hello"
Adding comments to your code is a good practice. It facilitates a clear understanding of the code for you and for others who read it. ---------------------------------------------------------------------------------------------- Variables
A variable is a named storage location for a value. It is called variable because the information stored in that location can be changed when the program is running. To assign a variable a value, use the equal sign. For example: x = 8
This assignment statement declares a variable named x and gives it the value 8. The equal sign is called the assignment operator. We can later use the variable name to access its value. For example, to output the value stored in the variable, we can use puts or print and refer to the variable name: x = 8 puts x
# outputs 8
Variable names may consist of alphanumeric characters and the underscore character (_), but cannot begin with a capital letter. ----------------------------------------------------------------------------------------------- Constants
Variables beginning with a capital letter are called constants. The value of a constant variable cannot be changed once it has been assigned. For example: MyNum = 42
# Trying to change the value produces a warning
MyNum = 8 # warning: already initialized constant MyNum ----------------------------------------------------------------------------------------------- Data Types
All variables in Ruby can be of all data types. Ruby automatically determines data type by the value assigned to the variable. For example:
x = 42 # integer y = 1.58 # floating point value z = "Hello" # string
You can reassign a different value to a variable at any time. To insert the value of a variable into a double quote string (a string is a sequence of characters, such as "Hello"), use the # symbol and curly brackets with the variable name. For example:
age = 42 puts "He is #{age} years old"
# outputs "He is 42 years old" ----------------------------------------------------------------------------------------------- Fill in the blanks to insert the value of the name variable into the string: name = "David" puts "Hello,#{name}" ----------------------------------------------------------------------------------------------- Doing Math
Math is an important part of programming. Ruby supports the following arithmetic operators: x = 5 y = 2
# Addition puts x+y # outputs 7
# Subtraction puts x-y # outputs 3
# Multiplication puts x*y # outputs 10
# Division puts x/y # outputs 2
When you divide two integer values, the result will be an integer, as shown in the above example. If you want to have a floating point result, one operand must be a floating point value: x = 5.0 y = 2 puts x/y # outputs 2.5 ----------------------------------------------------------------------------------------------- Modulus Operator
The modulus operator, represented by the percentage symbol (%), represents the remainder of a division operation. For example: x = 9 y = 5 puts x%y # outputs 4
9 divided by 5 is 1 with a remainder of 4. ----------------------------------------------------------------------------------------------- Exponent Operator
The ** represents the exponent operator for raising a number to a power to perform exponentiation. For example: a = 2 b = 5 puts a**b # this raises 2 to the power of 5 and outputs 32
The result is 32, as 2*2*2*2*2 = 32. All operators can also be used with floating point values. ----------------------------------------------------------------------------------------------- Shorthand Assignment Operators
All of the arithmetic operators have corresponding shorthand forms for assignment. For example, a = a + 8 can be written as a += 8. The same applies to the other 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
These are called self-assignment operators, as they perform an assignment and an arithmetic operation at the same time. ----------------------------------------------------------------------------------------------- Parallel Assignment
Ruby also supports parallel assignment of variables. This enables multiple variables to be initialized with a single line of code. For example: x = 10 y = 20 z = 30 may be more quickly initialized using parallel assignment: x, y, z = 10, 20, 30
Parallel assignment is also useful for swapping the values held in two variables: a, b = b, a ----------------------------------------------------------------------------------------------- Operator Precedence
Ruby evaluates a mathematical expression using an order of operations that is based on operator precedence. Exponentiation has the highest precedence followed by multiplication, division, and modulus from left to right, and then addition and subtraction from left to right. You can change the order of operations by using parentheses. For example: x = (3+2) * 4 puts x # outputs 20 ----------------------------------------------------------------------------------------------- Strings
As mentioned in the previous lessons, a string is text between single or double quotation marks. However, some characters can't be directly included in a string. For instance, single quotes can't be directly included in a single quote string, because this would designate the end of the string. Characters like these can be included in a string by using an escape sequence, which is indicated by a backslash (\):
text = 'Ruby\'s syntax is fun' puts text # outputs Ruby's syntax is fun
A string formed with double quotation marks can also include the \n escape sequence, which represents a new line. text = "Hello \n World" puts text
# Outputs: # Hello # World
Only the \' and \\ escape sequences can be used with single quote strings. ----------------------------------------------------------------------------------------------- String Interpolation
You can embed any Ruby expression inside a double quote string using #{ }, just as you did with variable names. Ruby evaluates the placeholders and replaces them with values: a = 5 b = 2 puts "The sum is #{a+b}" # outputs "The sum is 7" ----------------------------------------------------------------------------------------------- Concatenation
Strings can be joined using the + in a process called concatenation. When concatenating strings, it doesn't matter whether they've been created with single or double quotes.
a = "Hi " b = 'there' puts a+b # outputs "Hi there"
Even if your strings contain numbers, they are still added as strings rather than integers. Adding a string to a number produces an error, because even though they might look similar, they are two different entities: "1" is a string, whereas 1 is an integer. ----------------------------------------------------------------------------------------------- Repeating a String
Strings can be repeated using the * and an integer value. The order of the string and the integer does matter: the string has to come first. For example: a = "abc" puts a*3 # outputs "abcabcabc"
puts '5'*4 # outputs 5555 Try It Yourself
Strings can't be multiplied by other strings. ----------------------------------------------------------------------------------------------- Input
To get input from the user in Ruby, you use the gets method, which returns what the user types as a string. To store the input for later use, you can assign the return value to a variable. For example:
x = gets puts x
gets gets a line of text, including the new line at the end. If you do not want to include the new line, use the gets.chomp method:
puts "Enter your name" name = gets.chomp puts "Welcome, #{name}"
The value of the input is a string. To convert it to an integer, you can use the gets.to_i method. ----------------------------------------------------------------------------------------------- Booleans
In Ruby, there are two Boolean values: true and false. isOnline = true
userIsAdmin = false
Another value that you will often encounter is nil. It shows the absence of value. ----------------------------------------------------------------------------------------------- Booleans
If you try to evaluate a value other than true or false as a Boolean, Ruby will automatically treat it as a Boolean. When this is done, a non-Boolean value that evaluates to true is called "truthy" and a non-Boolean value that evaluates to false is called "falsey".
In Ruby only false and nil are falsey. Everything else is truthy (even 0 is truthy). Only true and false are Booleans. nil is not a Boolean. 0 is not a Boolean. The string "Hello" is not a Boolean. However, in a context where a Boolean is expected, Ruby evaluates them as Boolean (truthy and falsey). ----------------------------------------------------------------------------------------------- Comparison
A Boolean comparison using the == operator returns true when two operands are equal, and false when they are not:
a = 5 b = 8
puts a == b # false
puts a == 5 # true
Be careful not to confuse assignment (one equals sign) with comparison (two equals signs). ----------------------------------------------------------------------------------------------- Comparison
Another comparison operator, the not equal operator (!=), evaluates to true if the items being compared aren't equal, and false if they are.
For example: a = 8 b = 7
puts a != b # true ----------------------------------------------------------------------------------------------- Comparison
Ruby also has operators that determine whether one value is greater than or less than another. These operators are > and < respectively. Similarly, the greater than or equal to, and less than or equal to operators are >= and <=. For example:
puts 12 > 8 # true
puts 5 < 2 # false
puts 5 >= 5.0 # true
puts 3 <= 6 # true
There is also the .eql? method, which results in true only if both arguments have the same type and equal values. For example:
puts 3 == 3.0 # true # but puts 3.eql?(3.0) # false
3.eql?(3.0) is false because 3 is an integer and 3.0 is a float. Greater than and less than operators can also be used to compare strings lexicographically (the alphabetical order of words is based on the alphabetical order of their component letters). ----------------------------------------------------------------------------------------------- if Statements
You can use an if expression to execute code when a certain condition holds. If a conditional expression evaluates to true, the if code is carried out. Otherwise, the code is ignored. For example: a = 42
if a > 7 puts "Yes" end
# outputs "Yes"
The condition a > 7 is evaluated. When it is true, the statements inside the if are executed and the program outputs Yes. You can have multiple statements inside a single if expression. The end keyword is required to indicate the end of the if. ----------------------------------------------------------------------------------------------- if Statements
if expressions can be nested, one inside the other. This means that the inner if is the code part of the outer one. This is one way to see whether multiple conditions are satisfied. Keep in mind that once an if condition fails, the entire block is exited.
num = 16 if num > 7 puts "Greater than 7" if num < 42 puts "Between 7 and 42" end end # outputs: # Greater than 7 # Between 7 and 42
Each if has to have a corresponding end. ----------------------------------------------------------------------------------------------- else Statements
An else block in an if expression contains code that is called when the if conditional evaluates to false. For example:
age = 15 if age > 18 puts "Welcome" else puts "Too young" end
# outputs "Too young"
The end keyword is only needed for the if statement, as the else block is part of the if expression. ----------------------------------------------------------------------------------------------- elsif Statements
The elsif (short for else if) block is useful when you want to test multiple conditions. A series of if elsif expressions can have a final else block, which is called if none of the if or elsif expressions are true. For example:
num = 8 if num == 3 puts "Number is 3" elsif num == 10 puts "Number is 10" elsif num == 7 puts "Number is 7" else puts "Not found" end
When an elsif block executes the entire if expression is exited. ----------------------------------------------------------------------------------------------- unless
The unless expression is the opposite of an if expression. It executes code when a conditional is false. For example:
a = 42 unless a < 10 puts "Yes" else puts "No" end
# outputs "Yes"
You can use an else block with the unless, just like you did with the if expression. The end keyword is also required to close the block.
The if and unless modifiers can also be used to execute code.
a = 42 puts "Yes" if a > 10 puts "Yes" unless a < 10
This code before the if executes only if the condition evaluates to true. The code before the unless executes only if the condition is false. As you can see, Ruby code is comparably short and easy to read, making it a very intuitive programming language. ------------------------------------------------------------------------------------------------- Logical Operators
Logical operators are used to form more complex criteria that test more than one condition for an if expression. Ruby has three logical operators: and (&&), or (||), and not (!). A conditional using and evaluates as true if, and only if, both of its operands are true. Otherwise, the entire conditional evaluates to false. For example:
a = 42 b = 8 if a > 7 && b < 11 puts "Yes" end
# outputs "Yes"
Ruby can use words instead of the logical operator symbols (and, or, not), but those have lower precedence and are generally avoided. --------------------------------------------------------------------------------------------------- OR
The or (||) operator evaluates to true if either (or both) of its operands are true, and false if both operands are false. For example:
a = 3 b = 5 if a ==3 || b > 10 puts "Welcome" end
# outputs "Welcome" ------------------------------------------------------------------------------------------------------ NOT
The not (!) operator reverses the state of a single operand. The result of not true is false, and not false is true.
a = 7 puts !(a>5)
# outputs false
In this code, a>5 evaluates to true and then the not operator reverses it to false. You can chain together multiple conditions with the logical operators to check for multiple conditions. Parentheses can be used to group together separate conditions for clarity and to control the order of operations. For example:
(a>b && b < 100) || (a<b && b > 100) ------------------------------------------------------------------------------------------------------ case Statements
As we have seen in the previous lessons, we can check for multiple conditions using the if/elsif/else expression. A more simplified and flexible option is the case expression, which tests a value in when statements. For example:
a = 2
case a when 1 puts "One" when 2 puts "Two" when 3 puts "Three" end
# outputs "Two"
You can have as many when statements as you need for a single case. Note that the case expression must be closed with the end keyword. ------------------------------------------------------------------------------------------------------- case Statements
Multiple values can be tested within a single when by separating the values with commas. For example: age = 5
case age when 1, 2, 3 puts "Little baby" when 4, 5 puts "Child" end
# outputs "Child" -------------------------------------------------------------------------------------------------------- case Statements
An else statement can be provided to execute code if no when condition matches: age = 18
case age when 1, 2, 3 puts "Little baby" when 4, 5 puts "Child" else puts "Not a baby" end
# outputs "Not a baby"
Case statements allow us to more easily control program flow. If statements should always be used to determine if a conditional is true, and case statements are for when you need to make different decisions based on a value. -------------------------------------------------------------------------------------------------------- Loops
Loops are used to execute the same block of code a specified number of times. The while loop executes a block of code while its condition is true. For example: x = 0 while x < 10 puts x x += 1 end
This will output the numbers 0 to 9. After the loop control variable becomes 10, the condition x < 10 evaluates to false and the loop ends its execution. If we leave out the x+=1 statement, the loop will run forever, because the condition will remain true. This situation is called an infinite loop. -------------------------------------------------------------------------------------------------------- until Loops
The until loop is the opposite of a while loop: it will run while its condition is false. For example:
a = 0 until a > 10 puts "a = #{a}" a +=2 end
This will print all even numbers from 0 to 10. -------------------------------------------------------------------------------------------------------- Ranges
A range represents a sequence. 0 to 10, 56 to 82, and a to z are all examples of ranges. Ruby has special operators to create ranges with ease. These are the ''..'' and ''...'' range operators. The two-dot form creates an inclusive range, while the three-dot form creates a range that excludes the specified high value. For example: a = (1..7).to_a puts a # [1, 2, 3, 4, 5, 6, 7]
b = (79...82).to_a puts b # [79, 80, 81]
c = ("a".."d").to_a puts c # [a, b, c, d]
The to_a method is used to convert a range to an array, so we can output it. You will learn more about arrays in the coming lessons. -------------------------------------------------------------------------------------------------------- Ranges
Ranges can be used in case statements for when values. For example: age = 42
case age when 0..14 puts "Child" when 15..24 puts "Youth" when 25..64 puts "Adult" else puts "Senior" end Try It Yourself
Ranges are also useful in for loops. You will learn about them in the coming lesson. -------------------------------------------------------------------------------------------------------- for Loop
The for loop is a useful statement when you need to loop over a specific set of values, for example, a range. The for loop consists of an empty variable and a range. At each iteration of the loop, the empty variable will be assigned the corresponding element of the range. For example:
for i in (1..10) puts i end
This will output the numbers 1 to 10. During the first loop the variable i will be assigned the first value of the range, 1. On the second loop, it will be assigned to the value 2, and so on, until the end of the range. The for loop executes a block of code once for each element in the range. -------------------------------------------------------------------------------------------------------- break
The break statement can be used to stop a loop. For example:
for i in 1..5 break if i > 3 puts i end
# outputs: # 1 # 2 # 3
The loop stops executing when the condition i > 3 is met. --------------------------------------------------------------------------------------------------------- next
Similarly, the next statement can be used to skip one iteration of the loop and continue with the next one. For example:
for i in 0..10 next if i %2 == 0 puts i end
This will output only the odd numbers from 0 to 10 because the even numbers will skip the loop iteration. Ruby also has the redo statement, which causes the current loop iteration to repeat. The retry statement causes the whole loop to start again from the beginning. --------------------------------------------------------------------------------------------------------- loop do
Another looping statement in Ruby is the loop do statement. It allows code to execute until a break condition is achieved. For example:
x = 0 loop do puts x x += 1 break if x > 10 end
This will print the numbers 0 to 10. When x > 10 evaluates to true, the loop will stop. If we do not include a break condition, the loop will run forever. ---------------------------------------------------------------------------------------------------------- Arrays
An Array is essentially a list of numbered items. The items are declared in a comma-separated list and enclosed in square brackets. For example: items = ["Apple", "Orange", "Banana"]
The code above declares an array named items containing three strings.
Each item has its own unique index with the first item at index zero. To refer to an item, we need to refer to its index: puts items[0] # outputs "Apple"
You can assign any array element a new value by using its index: items[1] = "Kiwi"
This will assign the element with index 1 the value "Kiwi" A negative index is assumed relative to the end of the array. For example, an index of -1 indicates the last element of the array, -2 is the next to last element in the array, and so on. ----------------------------------------------------------------------------------------------------------- Adding Elements
An array can contain different types of elements: arr = [5, "Dave", 15.88, false]
puts arr[0] # 5 puts arr[1] # "Dave" puts arr[-1] # false
To add new elements to the array, you can use the << operator, which is typed as two less than signs:
arr << 8 puts arr
This will add an element with the value 8 to the end of the array. Alternatively, you can use the push and insert methods (we will learn more about methods in the coming module. For now, just remember that a method is code that performs an action).
arr.push(8)
This will add 8 to the end of the array.
The insert method allows you to insert the element at the desired position:
arr.insert(2, 8)
The code above will insert an element with value 8 at the position with index 2. -------------------------------------------------------------------------------------------------------------- Removing Elements
Similarly, there are pop and delete_at methods available to remove elements from the array. The pop method removes the last element of the array. For example: arr = [1, 2, 3] arr.pop print arr # [1, 2]
When used to output arrays, the puts method outputs one element per line, while print actually outputs brackets and a list of items.
You can use the delete_at method to specify the index of the element to be removed: arr = [2, 4, 6, 8] arr.delete_at(2) print arr # [2, 4, 8]
Remember, the index starts counting at 0. So the third element in the array has the index 2. ----------------------------------------------------------------------------------------------------------------- Array Ranges
You can even access a range within the array using the range operators: nums = [6, 3, 8, 7, 9] print nums[1..3] # [3, 8, 7] ----------------------------------------------------------------------------------------------------------------- Combining Arrays
You can add two arrays together: a = [1, 2, 3] b = [4, 5]
res = a + b print res # [1, 2, 3, 4, 5]
You can also subtract arrays, which will result in the first array removing any elements that also appear in second array. a = [1, 2, 3, 4, 5] b = [2, 4, 5, 6]
res = a - b print res #[1, 3]
The elements of an array can be duplicated using the * operator and an integer value. For example: [1, 2, 3] * 3 results in [1, 2, 3, 1, 2, 3, 1, 2, 3] ----------------------------------------------------------------------------------------------------------------- Boolean Operations
The & operator returns a new array containing the elements common to the two arrays, with no duplicates. a = [2, 3, 7, 8] b = [2, 7, 9]
print a & b # [2, 7]
The | operator returns a new array by joining the arrays and removing duplicates. a = [2, 3, 7, 8] b = [2, 7, 9]
print a | b # [2, 3, 7, 8, 9] ------------------------------------------------------------------------------------------------------------------ Moving Elements
The reverse method returns a new array containing the original array elements in reverse order. arr = [5, 3, 8] res = arr.reverse print res # [8, 3, 5]
You can also reverse the array in place using the reverse! method: arr = [1, 2, 3] print arr.reverse! ------------------------------------------------------------------------------------------------------------------- Array Methods
There are a number of other useful methods available for manipulating arrays. Here are some of the most used ones: array.length or array.size returns the number of elements in array. array.sort returns a new array with the elements sorted array.uniq returns a new array with duplicate values removed from array. array.uniq! removes duplicates in place. array.freeze safeguards the array, preventing it from being modified. array.include?(obj) returns true if obj is present in array, false otherwise. array.min returns the element with the minimum value. array.max returns the element with the maximum value. Most of the methods also work for strings, which can be thought of as arrays of characters.
A for loop is one way to iterate over an array of elements: arr = ["a", "b", "c"] for x in arr puts "Value: #{x}" end
This will loop through all the elements of the array and output them one per line. Ruby has a more elegant way of iterating over array elements. You will learn about iterators in the coming lessons. ----------------------------------------------------------------------------------------------------------------- Hashes
Hashes (sometimes known as associative arrays, maps, or dictionaries) are similar to arrays in that they are an indexed collection of elements. However, while you index arrays with integers, you can index a hash with anything. That is very useful when you want to map values, for example: "name" => "David". Here, "name" is the index (also called the key) which is mapped to the value "David". So, hashes represent key=>value pairs. A hash is created with comma separated keys and values inside curly brackets:
ages = { "David" => 28, "Amy"=> 19, "Rob" => 42 } puts ages["Amy"] # outputs 19
As you can see, the values are accessed using the same index syntax as with an array. Compared with arrays, hashes have one significant advantage: they can use any object as an index, even an array. For example: { [1,"jan"] => "January" } ----------------------------------------------------------------------------------------------------------------- Symbols
In the previous example we used strings as keys for the hash, but Ruby has a more elegant and faster way for creating and accessing hash indexes than using strings. Symbols are similar to strings, but they are immutable, meaning that they cannot be changed. A symbol is created using a colon and a name, for example: a = :id
In the code above :id is a symbol. You can also think of :id as meaning the name of the variable id, and plain id as meaning the value of the variable. So why use symbols instead of strings? Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once. -------------------------------------------------------------------------------------------------------------------- Hashes & Symbols
Uses of symbols includes using them as hash keys. For example: h = {:name=>"Dave", :age=>28, :gender=>"male"}
puts h[:age]
In the code above, symbols are used as keys for our hash. A shorter way of writing the same code as above is the following: h = {name:"Dave", age:28, gender:"male"}
puts h[:age]
In other programming languages hashes are called associative arrays or dictionaries. ----------------------------------------------------------------------------------------------------------------------- Hashes
There are useful methods available for manipulating hashes: hash.delete(key) removes the key-value pair from hash by key. hash.index(value) returns the key for the given value in hash, nil if no matching value is found. hash.invert creates a new hash, reversing keys and values from hash; that is, in the new hash, the keys from hash become values and values become keys. hash.keys returns a new array with keys from hash. hash.values returns a new array containing all the values of hash. hash.length returns the length of hash as an integer.
For example:
car = {brand:"BMW", year:2016, color:"red", length:205} car.delete(:length) puts car.values
# outputs: # BMW # 2016 # red ----------------------------------------------------------------------------------------------------------------------- Nested Arrays
Arrays can contain other arrays. These are called nested arrays. For example: arr = [ [1, 2, 3], [4, 5, 6] ] puts arr[1][2] # outputs 6
The arr array contains two arrays. So, arr[1][2] accesses the second array's third element, which is 6. There can be multiple levels of nesting. ------------------------------------------------------------------------------------------------------------------------ Nested Hashes
Hashes can also be nested. cars = { bmw: { year:2016, color:"red"}, mercedes: { year:2012, color:"black"}, porsche: { year:2014, color:"white"} }
puts cars[:bmw][:color] # outputs "red"
Hashes and arrays can have any level of nesting, but keep in mind that hashes and arrays with more than three dimensions are harder to manage. ------------------------------------------------------------------------------------------------------------------------ Iterators
As we have seen in the previous lessons, we can loop over arrays and hashes using for loops. Ruby provides more elegant looping methods called iterators. Iterators are used to create loops. The each iterator is one of the most used ones: arr = [2, 4, 6]
arr.each do |x| puts x end
The syntax might seem confusing at first, but you just need to remember the pipe symbols around the variable. The each iterator loops through all elements of the array and assigns the corresponding element to the variable inside the pipes with each iteration. This variable is called the block parameter. We can, for example, calculate the sum of all elements: arr = [2, 4, 6] sum = 0 arr.each do |x| sum += x end
puts sum # 12 ------------------------------------------------------------------------------------------------------------------------- Iterators
The each iterator can also be used with hashes: sizes = {svga:800, hd:1366, uhd:3840}
sizes.each do |key, value| puts "#{key}=>#{value}" end
In the example above, key and value are variables that get assigned to the corresponding values of the hash elements at each iteration. You can use any name for your variables.
The do and end keywords specify a block of code in Ruby. After the opening of the block, we have the block parameters within pipes ( | | ). Ruby provides a shorthand way of writing blocks: you can use curly braces to start and end code blocks. So the above example can be written shorter as: sizes = {svga:800, hd:1366, uhd:3840}
sizes.each { |key, value| puts "#{key}=>#{value}" }
The each iterator can also be used on ranges. For strings, you can use the each_char iterator to iterate over the characters. -------------------------------------------------------------------------------------------------------------------------- Iterators
There are also iterators available for numbers. The times iterator executes a loop the specified number of times: 10.times do puts "Hi" end
The code above will print "Hi" 10 times. -------------------------------------------------------------------------------- Letter Frequency
Let's create a program that will count the frequency of letters (number of occurrences) in a given string. First we need a string: # a sample text text = "I am learning Ruby and it is fun!"
text.downcase!
The downcase! method is used to convert all letters in the string to lowercase.
Next, we will need a hash to hold the letters as keys and the frequency of the letters as their corresponding values. For that we need to create an empty hash and set the default value for all values to 0.
freqs = {} freqs.default = 0
The default method is used to set the default value for the hash, meaning that any key that does not have a value assigned will be set to that value. ---------------------------------------------------------------------------------- Letter Frequency
Next, we will need to iterate over each character in the string and calculate the number of occurrences in the hash. We can do that using the each_char iterator: text.each_char { |char| freqs[char] += 1}
During each iteration, the char variable is assigned the corresponding character in our text string and then the value of that character's frequency is incremented in the freqs hash. So, for example, if the letter "c" appears twice in the text, freqs["c"] will be equal to 2 after the iterator executes. So, freqs will hold all the characters of the string with their corresponding occurrence number.
To show a nice result output in an alphabetical order, we can create a range of all letters and print their corresponding frequencies:
("a".."z").each {|x| puts "#{x} : #{freqs[x]}" }
We do this because not all letters of the alphabet are contained in our text.
The final code:
text = "I am learning Ruby and it is fun!" text.downcase! freqs = {} freqs.default = 0
text.each_char { |char| freqs[char] += 1}
("a".."z").each {|x| puts "#{x} : #{freqs[x]}" }
With just 6 lines of code we created a fully working letter frequency counter! --------------------------------------------------------------------------------------- Methods
In the previous lessons we have seen some examples of methods, such as the reverse method for arrays. A method is a set of statements that perform a specific task. You can define your own methods to perform your desired tasks. The def keyword is used to define a method.
def say puts "Hi" end
The above code defines a method called "say" that performs a simple output operation, printing "Hi". The method name should start with a lowercase letter, so it will not be confused with constants. ---------------------------------------------------------------------------------------- Calling Methods
Now that we have defined our method, we can call it by including its name in a line of code: def say puts "Hi" end
say # outputs "Hi"
You can call methods as many times as you need: say say say
Methods should be defined before calling them, otherwise Ruby will raise an error. You can also call methods inside other methods. ----------------------------------------------------------------------------------------------- Parameters
A method declaration can include parameters, which you define inside parentheses after the method name. For example: def sqr(x) puts x*x end
sqr(8) # outputs 64
The defined sqr method has one parameter (called x) and outputs its square. You can name your parameters anything you like. When calling the method, we "pass" the actual parameter value to the method using parentheses. The actual parameter value is called an argument. So, in the above example, x is the parameter of the method, while 8 is the argument. --------------------------------------------------------------------------------------------------- Multiple Parameters
A method can include multiple parameters, separated by commas. For example: def sum(a, b) puts a+b end
sum(7, 4) # outputs 11
sum(88, 164) # outputs 252
You can also pass variable arguments: def sum(a, b, c) puts a+b+c end
x = 2 y = 42 z = 6 sum(x, y, z) # outputs 50 ------------------------------------------------------------------------------------------------------- Default Parameters
You can also set default values for the parameters, so that the method will still work even if you do not provide all the arguments. For example: def sum(a, b=8) puts a+b end
Now our parameter b has a default value of 8. If we pass just one argument, the method will use the value 8 as the second argument: x = 5 sum(x) # outputs 13
We can also pass two arguments as before, and the method will work as expected. The default value only comes into play when no value for that argument is provided. ---------------------------------------------------------------------------------------------------------- Fill in the blanks to define a method with two parameters with default values 10 and 42 respectfully.
def doSmth(x = 10, y=42) puts x*y end ---------------------------------------------------------------------------------------------------------- Parameters
You can also leave off the parentheses when using methods. For example, sum(x, y) can be written as sum x, y.
def sum x, y puts x+y end
sum 6, 9
This leads to more fluid reading of code, but sometimes it can be confusing.
Let's create a program that prompts the user to enter a name and then outputs a greeting based on the input.
def greet(name="") if name=="" puts "Greetings!" else puts "Welcome, #{name}" end end
greet(gets.chomp)
We defined a method called greet that takes one parameter and outputs a message based on that parameter. Then we called the greet method passing user input as the argument. Reminder: gets.chomp is used to take user input and strip the newline at the end of the input. ---------------------------------------------------------------------------------------------------------- Optional Parameters
You can also define methods that take any number of arguments using the following syntax: def someMethod(*p) puts p end
someMethod(25, "hello", true)
Now you can pass any number of arguments to the method. In the code above p is an array. When you pass arguments to the method, they become elements of p. If you call the method without any arguments, the array p will be empty.
This technique allows optional parameters for a method, for example:
def myMethod(a, b, *c) #some code end
myMethod has two mandatory parameters and an optional one. Therefore, you can pass two or more arguments to the method. ----------------------------------------------------------------------------------------------------------- Returning Values
Until now all the methods we have defined output values. Sometimes you need a method to perform an action and return the result so that the returned value can be used further in the program (for example, by assigning the return value to a variable). The return keyword is used to return a value from a method. For example: def sum(a, b) res = a+b return res end
x = sum(5, 23) puts x
The sum method returns the sum of its parameters, which is then assigned to variable x. ----------------------------------------------------------------------------------------------------------- Returning Values
You can return multiple values from a method by separating them with commas in the return statement. For example: def squares(a, b, c) return a*a, b*b, c*c end
arr = squares(2, 3, 6) puts arr Try It Yourself
The result is an array containing the returned values.
Even when you do not explicitly include a return statement in your method, Ruby always returns the evaluated result of the last line of the method that is executed. For example:
def demo(a, b) a = b-2 b = a-3 end
puts demo(5, 6) # outputs 1
The above method will return the result of its last expression, which is b = a-3. Any code in the method after a return statement will not execute, because a method stops executing once the return is executed. For example:
def demo(a) a = 5 return a a = 9 end
The method above will return the value 5. The statement a=9 is ignored. -------------------------------------------------------------------------------------------------------- Chaining Methods
Because all methods return a value, we can chain multiple methods together, as well as chain methods with iterators. For example:
def square(x) x*x end
square(4).times {puts "Hi"}
In the code above we chained our defined square method with the times iterator. As square(4) will return 16, the loop will run and output "Hi" 16 times. If anywhere along the chain there is a nil or an error, the entire chained call will break down. --------------------------------------------------------------------------------------------------------- Methods as Arguments
We can also pass methods as arguments to other methods. The returned values of those methods will be used as the actual values for the arguments, for example:
def add(a, b) a+b end
def mult(a, b) a*b end
x = mult(add(2, 3), add(4, 7)) puts x # outputs 55
We defined two methods, add and mult, and passed two calls to the add method as arguments to mult. Lets break down the code: add(2, 3) returns 5 add(4, 7) returns 11 so mult(add(2, 3), add(4, 7)) is the same as mult(5, 11) which returns 55. --------------------------------------------------------------------------------------------------------- Variable Scope
Scope defines where in a program a variable is accessible. Ruby has four types of variable scope: local, global, instance and class.
Local variables are local to the code construct in which they are declared. For example, a local variable declared in a method or within a loop cannot be accessed outside of that loop or method. Local variable names must begin with either an underscore or a lowercase letter. For example:
def calc(x) y = 2 puts x*y end
In the above code x and y are both local variables. They are accessible only inside the calc method. If we try to access them outside of the method we will get an error:
def calc(x) y = 2 puts x*y end
puts y # output: "undefined local variable or method 'y' "
The same applies to loops and iterators:
arr = [1, 2, 3] arr.each {|x| puts x}
x is a local variable available only in the iterator block. You can have different variables with the same name in different scopes. --------------------------------------------------------------------------------------------------- Global Scope
Global variables in Ruby are accessible from anywhere in the Ruby program, regardless of where they are declared. Global variable names are prefixed with a dollar sign ($). For example: $x = 42
def change $x = 8 end
change puts $x # outputs 8
As you can see the $x global variable is accessible in the whole program. Use of global variables is strongly discouraged. The problem with global variables is that, not only are they visible anywhere in the code, but they can also be changed from anywhere in the application. This can result in hard to find bugs. ----------------------------------------------------------------------------------------------------- Variable Scope
So why does scope exist? Why couldn't all variables be accessible everywhere so we wouldn't have to care about their scope? First of all, there are naming issues: if you have a big program, you'd have to give all of your variables unique names to avoid conflicts. Imagine keeping track of thousands of variable names. Then, there are access issues: it's very hard to track who changes what when everyone has the ability to do so. Scope makes the program more predictable and secure. The other two types of scope in Ruby are instance and class. We will learn about them in the next module. ------------------------------------------------------------------------------------------------------ Recursion
Another way of creating loops is recursion. It involves the concept of methods calling themselves. It is used to solve problems that can be broken up into easier sub-problems of the same type.
A classic example of a method that is implemented recursively is the factorial method, which finds the product of all positive integers below a specified number. For example, 5! (5 factorial) is 5 * 4 * 3 * 2 * 1 (120). To implement this recursively, notice that 5! = 5 * 4!, 4! = 4 * 3!, 3! = 3 * 2!, and so on. Generally, n! = n * (n-1)!. Furthermore, 1! = 1. This is known as the base case, as it can be calculated without performing any more factorials. Below is a recursive implementation of the factorial method.
def fact(n) if n<= 1 1 else n * fact( n - 1 ) end end
puts fact(5) # outputs 120
The if n<=1 block acts as the base case. The base case is the exit condition of the recursion. Ruby is fast! Try to run the above code for a larger number and see the output. -------------------------------------------------------------------------------------------------------- Recursion
Recursive methods can become infinite, just like infinite loops. These often occur when you forget to implement the base case. Below is an incorrect version of the factorial method. It has no base case:
def fact(n) n * fact( n - 1 ) end
puts fact(5) # outputs "stack level too deep (SystemStackError)"
Remember, an important key concept with recursion is to define and include the base case that makes the recursion stop. ---------------------------------------------------------------------------------------------------------- Object Oriented Programming
Ruby is a pure object-oriented language, which means everything in Ruby is an object. Even simple numbers, strings and Booleans are objects.
In programming, objects are independent units, and each has its own identity, just as objects in the real world do. An apple is an object; so is a mug. Each has its own unique identity. It's possible to have two mugs that look alike, but they are still separate, unique objects.
To create objects we use classes. A class defines the data and actions associated with an object, but is separate from the object itself. In other words, a class can be thought of as an object's blueprint, description, or definition. For example, there can be many cat objects of the single class Cat. Just like in the real world, where you can use a blueprint to construct multiple buildings, in programming you can use the same class as a blueprint for creating multiple objects. -------------------------------------------------------------------------------------------------------------
|