Contents
Ruby Methods
Ruby Methods
A method in Ruby is a collection of statements that perform a specific task and return a result. Methods help to avoid repetition, allowing code reuse by defining the logic once and calling it multiple times. Methods in Ruby are defined using the def
keyword, and the method definition is ended with the end
keyword. Method names in Ruby are typically in lowercase.
Defining and Calling a Method
A method in Ruby must be defined before it is called. It can be called simply by using its name.
Syntax:
def method_name
# Statement 1
# Statement 2
end
Example:
# Ruby program to demonstrate defining and calling a method
# Here 'welcome' is the method name
def welcome
# Statement to be displayed
puts "Hello! Welcome to Ruby Programming."
end
# Calling the method
welcome
Output:
Hello! Welcome to Ruby Programming.
Passing Parameters to Methods
In Ruby, parameters are passed to methods by simply listing them within parentheses. Default values can also be provided for parameters.
Syntax:
def method_name(param1, param2)
# Statements
end
Example:
# Ruby program to demonstrate passing parameters to a method
# Defining the method 'display_info' with two parameters
def display_info(name = "Ruby", level = "Beginner")
puts "Language: #{name}"
puts "Level: #{level}"
end
# Calling the method with parameters
display_info "Python", "Intermediate"
puts ""
# Calling the method without passing parameters
display_info
Output:
Language: Python
Level: Intermediate
Language: Ruby
Level: Beginner
Variable Number of Parameters
Ruby allows methods to accept a variable number of parameters. This is useful when the exact number of arguments is unknown at the time of defining the method. The *
symbol is used to define a variable number of parameters.
Syntax:
def method_name(*args)
# Statements
end
Example:
# Ruby program to demonstrate a method that takes a variable number of arguments
# Defining the method 'show_items' that can take any number of arguments
def show_items(*items)
puts "Number of items: #{items.length}"
# Display each item using a loop
items.each_with_index do |item, index|
puts "Item #{index + 1}: #{item}"
end
end
# Calling the method with multiple arguments
show_items "Apple", "Banana", "Cherry"
# Calling the method with a single argument
show_items "Orange"
Output:
Number of items: 3
Item 1: Apple
Item 2: Banana
Item 3: Cherry
Number of items: 1
Item 1: Orange
Return Statement in Methods
The return statement is used to return one or more values from a method. By default, a method returns the value of the last executed statement. The return keyword is optional and can be omitted unless you want to explicitly specify the return values.
Example:
# Ruby program to demonstrate the return statement in a method
# Defining the method 'calculate_sum'
def calculate_sum
# Variables within the method
num1 = 25
num2 = 75
# Calculating sum
sum = num1 + num2
# Returning the sum
return sum
end
# Calling the method and printing the result
puts "The sum is: #{calculate_sum}"
Output:
The sum is: 100
Method Visibility in Ruby
In Ruby, method visibility determines how methods in a class can be accessed. Methods can be declared as public, private, or protected. By default, methods are public unless explicitly specified otherwise. Method visibility is used to achieve data abstraction, meaning it shows only the necessary details while hiding the internal implementation.
Access Modifiers in Ruby
Ruby provides three types of access modifiers to control method visibility:
- Public Access Modifier
- Protected Access Modifier
- Private Access Modifier
1. Public Access Modifier: Methods defined with public are accessible from any part of the program. By default, all methods in a class are public unless specified otherwise.
Example 1:
# Program to demonstrate public access modifier
class Example
# Methods are public by default
def public_method1
puts "public_method1 called!"
end
# Using the public keyword explicitly
public
def public_method2
puts "public_method2 called!"
end
end
# Creating an object
obj = Example.new
# Calling methods
obj.public_method1
obj.public_method2
Output:
public_method1 called!
public_method2 called!
In this example, two public methods (public_method1
and public_method2
) are defined in the Example
class and can be called from outside the class.
2. Protected Access Modifier: Methods defined as protected
can only be called within the class they are defined in and by instances of that class or its subclasses.
Example 2:
# Program to demonstrate protected access modifier
# Superclass
class Parent
# Using the protected keyword
protected
# Protected method
def protected_method
puts "protected_method called!"
end
end
# Subclass
class Child < Parent
def public_method
# Calling the protected method from within the subclass
self.protected_method
end
end
# Creating an object of the subclass
obj = Child.new
# Calling the public method which in turn calls the protected method
obj.public_method
Output:
protected_method called!
Here, the protected_method in the Parent class is called within the public_method of the Child subclass.
3. Private Access Modifier: Private methods can only be called within the class they are defined in. They cannot be called with an explicit receiver, even if that receiver is self.
Example 3:
# Program to demonstrate private access modifier
class Example
# Using the private keyword
private
# Private method
def private_method
puts "private_method called!"
end
# Public method
public
def public_method
# Calling the private method within the public method
private_method
end
end
# Creating an object
obj = Example.new
# Calling the public method which in turn calls the private method
obj.public_method
Output:
private_method called!
In this example, private_method
is a private method and can only be called within the class. It is invoked from the public_method
of the same class.
Method Visibility in Action
Here is an example demonstrating method visibility by defining multiple public, protected, and private methods in a superclass and a subclass.
Example 4:
# Program to illustrate method visibility
# Superclass
class Parent
private
# Private method
def private_method
puts "private_method called!"
end
protected
# Protected method
def protected_method
puts "protected_method called!"
end
public
# Public methods
def public_method1
puts "public_method1 called!"
end
def public_method2
# Calling protected and private methods within a public method
protected_method
private_method
end
end
# Subclass
class Child < Parent
# Public method
def public_method3
# Calling protected method from within the subclass
protected_method
end
end
# Creating objects
obj1 = Parent.new
obj2 = Child.new
# Calling methods
puts "\nParent methods: \n"
obj1.public_method1
obj1.public_method2
puts "\nChild methods: \n"
obj2.public_method1
obj2.public_method3
Output:
Parent methods:
public_method1 called!
protected_method called!
private_method called!
Child methods:
public_method1 called!
protected_method called!
Recursion in Ruby
Recursion is a process where a function calls itself directly or indirectly. The function that performs this operation is called a recursive function. Recursion is beneficial because it breaks down problems into smaller, more manageable sub-problems, resulting in cleaner and more efficient code. In Ruby, we can use loops to repeat actions, but recursion offers an elegant alternative in many situations.
Why Use Recursion?
In Ruby, recursion plays a significant role in solving real-world problems by introducing a way to handle complex data through recursive functions. Although loops can achieve similar results, recursion often provides a more intuitive and elegant solution.
Table of Contents
1. Iterative Code
2. Recursive Code
3. Benefits and Limitations of Recursion
1. Iterative Code: One of the simplest problems we can solve iteratively is summing up the elements in an array. In an iterative approach, a loop is used to add each element to a cumulative total.
Example:
# Iterative method to sum an array of numbers
def iterative_sum(numbers)
sum = 0
numbers.each do |number|
sum += number
end
sum
end
# Calling the method and printing the result
puts iterative_sum([1, 2, 3, 4, 5])
Output:
15
2. Recursive Code: In a recursive solution, the function calls itself with a subset of the problem until a base case is met. For summing an array, the base case occurs when the array is empty.
Example:
# Recursive method to calculate the sum of all numbers in an array
def recursive_sum(numbers)
# Base Case: If the array is empty, return 0
return 0 if numbers.empty?
# Recursive call: Adding each element to the total by calling the method again
sum = numbers.pop
return sum + recursive_sum(numbers)
end
puts recursive_sum([1, 2, 3, 4, 5])
Output:
15
Understanding Recursive Steps: Factorial Calculation
To understand recursion more deeply, let’s calculate the factorial of a number using a recursive method. The factorial of a number is the product of all positive integers less than or equal to that number.
Example:
# Ruby code for calculating the factorial of a number recursively
def recursive_factorial(n)
# Base Case: Return 1 if n is 0 or 1
return 1 if n <= 1
# Recursive call: Multiply current number with the factorial of (n-1)
return n * recursive_factorial(n - 1)
end
# Calling the method
puts recursive_factorial(5)
Example:
120
Explanation:
The method first calls itself with the number 5, which in turn calls itself with 4, then 3, and so on until it reaches 1. The calculations proceed as follows:
recursive_factorial(5) = 5 * recursive_factorial(4)
= 5 * 4 * recursive_factorial(3)
= 5 * 4 * 3 * recursive_factorial(2)
= 5 * 4 * 3 * 2 * recursive_factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
Recursive Code Example: Fibonacci Number Calculation
Another classic example of recursion is the calculation of the nth Fibonacci number. The base cases occur when n
is less than 2, where the Fibonacci number is simply n
. Otherwise, the Fibonacci number is the sum of the two preceding ones.
Example:
# Recursive method to calculate the nth Fibonacci number
def recursive_fibonacci(n)
return n if n < 2
# Recursive call: Sum of (n-1)th and (n-2)th Fibonacci numbers
recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2)
end
puts recursive_fibonacci(5)
Output:
5
3. Benefits and Limitations of Recursion
Benefits:
- Recursion can simplify the solution of problems by breaking them into smaller sub-problems.
- It often leads to cleaner and more readable code for complex tasks like tree traversal, factorials, Fibonacci sequences, and more.
Limitations:
- Recursion in Ruby can lead to a
SystemStackError: stack level too deep
when dealing with large inputs. - For problems with large datasets, recursion may be less efficient than iterative solutions due to the risk of stack overflow.
Ruby Hook Methods
Ruby Hook Methods are special methods that are triggered in response to certain actions, such as adding a method, including a module, or subclassing a class. They allow the extension of the behavior of objects and classes at runtime. These methods are not predefined, but programmers can define and use them to customize class or module behavior dynamically.
Hook methods enable a more flexible approach to modify and extend the fundamental workings of methods, modules, and classes in Ruby.
Common Ruby Hook Methods
1. Included
2. Prepended
3. Extended
4. Inherited
5. method_missing
Modules in Ruby
Before delving into the hook methods, it’s essential to understand modules in Ruby. Modules are containers for methods and constants that can be mixed into classes. This allows for code reuse and modularity. Hook methods often interact with modules to alter or extend class behavior.
1. Included:
The included
method is called when a module is included in another class or module. It is used to apply the contents of a module to instances of a class. When the module is included, the included
method executes automatically.
Example:
# Declaring a module
module WelcomeMessage
def self.included(target)
puts "The #{target} has been greeted with a warm welcome!"
end
end
# Class where the module is included
class User
include WelcomeMessage # Using the include statement
end
Output:
The User has been greeted with a warm welcome!
2. Prepended:
Introduced in Ruby 2.0, prepend
works similarly to include
, but with a slight difference. It allows you to override methods defined in a class with those from the prepended module. This concept involves altering the target class’s behavior by using methods from the module.
Example:
# Module to demonstrate prepend method
module Language
def self.prepended(target)
puts "#{self} has been prepended to #{target}"
end
def description
"The language used is Ruby."
end
end
# Class where the module is prepended
class Programming
prepend Language # The module Language is prepended
end
# Calling the method
puts Programming.new.description
Output:
Language has been prepended to Programming
The language used is Ruby.
3. Extended:
The extended
method is used when you want to mix a module’s methods into a class itself, not just into its instances. Unlike include
, which mixes methods into an instance, extend
applies methods directly to the class.
Example:
# Module to demonstrate the extend method
module Framework
def self.extended(target)
puts "#{self} was extended by #{target}"
end
def description
"This framework is based on Ruby."
end
end
# Class where the module is extended
class Software
extend Framework # Extending the module Framework
end
# Calling the method
puts Software.description
Output:
Framework was extended by Software
This framework is based on Ruby.
4. Inherited:
The inherited
hook method is called when a class is subclassed. This is particularly useful in object-oriented programming when a subclass inherits attributes and methods from its parent class.
Example:
# Parent class
class Vehicle
def self.inherited(subclass)
puts "#{subclass} is a subclass of Vehicle"
end
end
# Subclass
class Car < Vehicle
end
Output:
Car is a subclass of Vehicle
5. method_missing:
The method_missing
hook method is called when an object is sent a message (method call) that it does not understand (i.e., when a method that doesn’t exist is called on an object).
Example:
# Main class
class Language
def method_missing(method_name, *args)
"#{method_name} is not defined for #{self}"
end
def known_method
"This method is defined."
end
end
instance = Language.new
# Calling a method that exists
puts instance.known_method
# Calling a method that does not exist
puts instance.unknown_method
Output:
This method is defined.
unknown_method is not defined for #
Ruby Range Class Methods
In Ruby, the Range class allows you to represent a set of values, defined by a starting and an ending point. Ranges can be created using a literal (start..end
for inclusive ranges or start...end
for exclusive ranges) or by using Range.new
.
Here are the key concepts and examples for Ruby ranges:
Creating Ranges
You can create a range with the following syntaxes:
- Inclusive Range (
..
): Includes the end value. - Exclusive Range (
...
): Excludes the end value.
(1..6).to_a # => [1, 2, 3, 4, 5, 6]
(1...6).to_a # => [1, 2, 3, 4, 5]
Class Method
Range.new(start, end, exclusive = false)
: This class method creates a new range. If the third parameter is true
, the range excludes the end value; otherwise, it includes the end.
a = Range.new(1, 5)
b = Range.new(1, 5)
puts a == b # => true
Instance Methods
==
: Checks if two ranges are equal by comparing their start, end, and exclusion flag.
case 30
when 1...50
puts "In range"
else
puts "Out of range"
end
# Output: In range
begin
: Returns the first object in the range.
range = (3..10)
puts range.begin # => 3
end
: Returns the last object in the range.
range = (3..10)
puts range.end # => 10
each
: Iterates over each element of the range.
(1..5).each { |i| print i, " " }
# Output: 1 2 3 4 5
first
: Returns the first object in the range (same asbegin
).
range = (5..10)
puts range.first # => 5
last
: Returns the last object in the range (same asend
).
range = (5..10)
puts range.last # => 10
member?
orinclude?
: Checks if a value is a member of the range.
range = (1..10)
puts range.member?(5) # => true
puts range.include?(15) # => false
exclude_end?
: Returnstrue
if the range excludes the end value,false
otherwise.
range = Range.new(1, 5, true)
puts range.exclude_end? # => true
step
: Iterates over the range, stepping by a given value.
(1..10).step(2) { |i| print i, " " }
# Output: 1 3 5 7 9
true
false