Ruby: Methods, Control flow, Loops

Fri Nov 17 2023

Delving a bit deeper into Ruby.

Methods

A method is a section of code created to perform a task in a program that you can reuse. A method consists of a header, a body (indented by convention) and the end keyword. The code from inside a method needs to be called in order to be executed.

def greet
puts "Hello!"
end

greet // Hello!

Let's greet a certain person and use a parameter.

def greet(person)
  puts ("Hello " + person + "!")
end

greet("Nico")   // Hello Nico!

Splat arguments

Splat arguments are arguments that start with an asterisk *. The asterisk tells the program that the method can receive one or more arguments.

def whats_up(greeting, *friends)
  friends.each { |friend| puts "#{greeting}, #{friend}!" }
end
      
whats_up("What's up", "Nico", "Enzo", "Mario")
    // What's up, Nico!
       What's up, Enzo!
       What's up, Mario!

Return

Return is used inside a method, when we want this method to give us back a value, which means executing another method. Any code that goes under the line with return is not going to be executed.

def sqrt(n)
    return n * n
end

puts sqrt(3)    // 9

Blocks

Blocks are similar to methods, but they don't have a name and cannot be saved to a variable. Another difference is that they run only once and cannot be called upon again. Blocks can be defined with either the keywords do and end or with { }(curly braces). A method can take a block as a parameter, for example when using each.

["enzo", "nico"].each {|string| puts "#{string[0].upcase}#{string[1..-1]}"} 
    // Enzo
       Nico

Collect

.collect returns a copy of an array. However, we can mutate the array if we add an exclamation mark to collect.

numbers = [1, 2, 3, 4, 5]

tripled_numbers = numbers.collect { |n| n * 3 }

print tripled_numbers, " "              // [3, 6, 9, 12, 15]  

Yield

If a method has the keyword yield, it means that for that particular line of code, it accepts a block of code specified outside fo this method.
def double(n)
    yield(n)
end

 double(16) { |n| puts n * 2 }

Proc

A proc is what happens when we give a bloc a name. We use them so we don't have to repeat the same code again and again. To define a block we call Proc.new and pass in our block. To call it we add & to the proc's name.

multiples_of_3 = Proc.new do |n|
  n % 3 == 0
end

print (1..30).to_a.select(&multiples_of_3)              // [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

.call

We use .call method to call procs, as just defining them will not run the code from inside of them.

hi = Proc.new { puts "Hello!" }

hi.call

There are also other ways of calling a lambda, but using the .call method gives more clarity.

Symbols and procs

numbers_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

strings_array = numbers_array.map(&:to_s)

print  strings_array            // ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] 

Lambdas

Lambdas are nearly identical to procs. They define blocks and their parameters and can be saved to a variable. Lambdas check the number of the passed arguments, while procs don't. Procs will ignore extra arguments and pass nil to the missing ones, but lambdas will throw an error.

You can either use the word "lambda" or the symbol "->".

lambda { puts "Hello!" } OR lambda {|param| BLOCK }

Sorting

sort! will sort our arrays of numbers and strings alphabetically or ascending. This method, however, accepts a second argument, if we want to specify how the items should be compared.

my_array = [3, 4, 8, 7, 1, 6, 5, 9, 2]

print my_array.sort! // [1, 2, 3, 4, 5, 6, 7, 8, 9]

print my_array.sort! { |num1, num2| num2 <=> num1 }        // [9, 8, 7, 6, 5, 4, 3, 2, 1]

There is a combined comparison operator that looks like this: <=> . It returns 0 if the first operand equals the second. If the first operand is greater than the second it returns 1, and if the first operand is less than the second it returns -1.


day_1 = "Monday"
day_2 = "Tuesday"

puts day_1 <=> day_2      // -1

Control Flow

If, elsif, else, end

age = 13

if age < 1
  puts "You're an infant"
elsif age >= 1 and age < 13
  puts "You're a child"
elsif age >= 13 and age < 18
  puts "You're a teenager"
else
  puts "You're an adult"
end
             // You're a teenager 

We can also use the one-liner syntax which looks like so:

puts "You're an infant" if age < 1

Unless

sleepy = true

unless sleepy
  puts "I'm in front of the computer"
else
  puts "Bed time!"
end
             // Bed time! 

Unless also has a one-line syntax:

puts "I'm in front of the computer" unless sleepy

Boolean operators: &&, ||, !

true && true      // true
true && false     // false
false && true     // false
false && false    // false

true || true     // true
true || false    // true
false || true    // true
false || false   // false

!true            // false
!false           // true

Let's have a look at some examples.

boolean_1 = (3 < 4 || false) && (false || true)

print boolean_1       // true

boolean_2 = !true && (!true || 100 != 5**2)

print boolean_2       // false

Case Expressions

Case expressions are a useful solution when we check one value against a number of other values.

def get_day(day)
day_name = ""

case day
  when "mon"
    day_name = "Monday"
  when "tue"
    day_name = "Tuesday"
  else
    day_name = "Incorrect input"
  end

  return day_name
end

puts get_day("tue")           // Tuesday

Conditional Assignment

If we want to assign a variable only if it hasn't already been assigned, we can use the conditional assignment operator: ||=

name = nil
print name                 // nothing prints

name ||= "Lena"
print name                 // Lena

name ||= "Eric"
print name                 // Lena

Concatenation operator <<

<< is used to push to arrays.

[1, 2, 3] << 4                // [1, 2, 3, 4]
    
alphabet = ["a", "b", "c"]
alphabet << "d"                // ["a", "b", "c", "d"]

.upto and .downto

If we know what is the top or the bottom number that we are going to count to we can use .upto or .downto. What is more, these methods will also work with letters.

15.downto(5) { |num| print num, " " }            // 15 14 13 12 11 10 9 8 7 6 5

"A".upto("Z") { |letter| print letter, " " }     // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 

.respond_to?

In Ruby we can check if we can call a method on an object. respond_to? returns true if we can.

Since we cannot turn an array into a symbol...

[1, 2, 3].respond_to?(:to_sym)       // false

Loops and iterators

While

index = 1                          // we start from 1

while index <= 5                   // we stop when we get to 5
print index                        // we execute this code 
  index += 1                       // while adding 1 with each loop 
end                // 12345 

Until

index = 0                          // we start from 0

until index == 6                   // we stop at 6
print index = index + 1            // we execute this code until index equals 6
end                // 123456

For

The for loop is useful when we don't know how many times we will be looping.

Syntax:

for num in 1...10
  print num
end             // 123456789

Three dots mean that the final number will be excluded from the count.
Two dots mean that the final number should be included.

The Loop Method

In Ruby, { } (curly braces) are generally interchangeable with the keywords do and end. The keyword break breaks a loop whe the condition is met.

i = 0
loop do
  i += 1
  print "#{i}"
  break if i > 5
end

Next

We use the next keyword to skip some steps in the loop. In the example below we only print odd numbers.

for i in 1..5
  next if i % 2 == 0
  puts i
end                 // 1
                       3
                       5

.each

object.each { |item|
# Do something
}

OR

object.each do |item|
# Do something
end

.times

The .times iterator can perform a task on each item in an object a specified number of times.

5.times { print "hey " } // hey hey hey hey hey

Sources:

Codecademy

Illustration for the Ruby: Methods, Control flow, Loops

Comments (0)


Be the first to leave a comment