Skip to content

Ruby Operators

Operators are symbols or keywords that perform specific operations. Ruby provides a rich set of operators that make code more concise and expressive. This chapter will introduce in detail the various operators in Ruby and how to use them.

🎯 Operator Overview

Operators in Ruby can be divided into the following categories:

  1. Arithmetic Operators - Perform mathematical operations
  2. Comparison Operators - Compare values
  3. Logical Operators - Perform boolean logic operations
  4. Assignment Operators - Assign values to variables
  5. Bitwise Operators - Perform bit-level operations
  6. Range Operators - Create range objects
  7. Other Operators - Operators for special purposes

➕ Arithmetic Operators

Basic Arithmetic Operators

ruby
# Addition
puts 10 + 5        # 15
puts "Hello" + " " + "World"  # Hello World

# Subtraction
puts 10 - 5        # 5

# Multiplication
puts 10 * 5        # 50
puts "Ruby" * 3    # RubyRubyRuby

# Division
puts 10 / 5        # 2
puts 10 / 3        # 3 (integer division)
puts 10.0 / 3      # 3.3333333333333335 (floating point division)

# Modulo (remainder)
puts 10 % 3        # 1
puts 10.5 % 3      # 1.5

# Exponentiation
puts 2 ** 3        # 8
puts 9 ** 0.5      # 3.0 (square root)

Unary Operators

ruby
# Positive sign
puts +5            # 5

# Negative sign
puts -5            # -5
puts -(-5)         # 5

# Increment/Decrement (Ruby doesn't have ++ and -- operators)
counter = 5
counter += 1       # Equivalent to counter = counter + 1
puts counter       # 6

counter -= 1       # Equivalent to counter = counter - 1
puts counter       # 5

🔄 Comparison Operators

Basic Comparison Operators

ruby
# Equality comparison
puts 5 == 5        # true
puts 5 == "5"      # false (different types)
puts 5.eql?("5")   # false (strict equality)
puts 5 === 5       # true (equality in case statements)

# Inequality comparison
puts 5 != 3        # true
puts 5 != 5        # false

# Magnitude comparison
puts 5 > 3         # true
puts 5 < 3         # false
puts 5 >= 5        # true
puts 5 <= 3        # false

# String comparison
puts "apple" < "banana"    # true (lexicographical)
puts "Apple" < "apple"     # true (uppercase letters have smaller ASCII values)

Special Comparison Operators

ruby
# Safe comparison operator <=>
result = 5 <=> 3   # 1 (left is greater than right)
result = 5 <=> 5   # 0 (equal)
result = 3 <=> 5   # -1 (left is less than right)

# Application in range checking
case 75
when 90..100
  puts "Excellent"
when 80...90
  puts "Good"
when 60...80
  puts "Pass"
else
  puts "Fail"
end
# Output: Pass

Object Comparison

ruby
# equal? - Check if it's the same object
a = "hello"
b = "hello"
c = a

puts a.equal?(b)   # false (different objects)
puts a.equal?(c)   # true (same object)

# == - Check if values are equal
puts a == b        # true (values are equal)

# eql? - Check if both value and type are equal
puts 1 == 1.0      # true
puts 1.eql?(1.0)   # false (different types)

🔣 Logical Operators

Boolean Logic Operators

ruby
# And operations (and, &&)
puts true && true     # true
puts true && false    # false
puts false && true    # false
puts false && false   # false

# Or operations (or, ||)
puts true || true     # true
puts true || false    # true
puts false || true    # true
puts false || false   # false

# Not operations (not, !)
puts !true            # false
puts !false           # true
puts !!true           # true (double negation)

Priority Differences

ruby
# && and || have higher priority than and and or
result1 = true || false and false
puts result1          # true (equivalent to (true || false) and false)

result2 = true or false and false
puts result2          # false (equivalent to true or (false and false))

# Recommended to use && and || to avoid confusion

Short-Circuit Evaluation

ruby
# && short-circuit: If left is false, don't evaluate right
def expensive_operation
  puts "Executing expensive operation"
  true
end

false && expensive_operation  # Won't output "Executing expensive operation"

# || short-circuit: If left is true, don't evaluate right
true || expensive_operation   # Won't output "Executing expensive operation"

# Use short-circuit evaluation to set default values
name = nil
display_name = name || "Anonymous User"
puts display_name             # Anonymous User

# Safe navigation
user = nil
user_name = user&.name || "Default Name"
puts user_name                # Default Name

📝 Assignment Operators

Basic Assignment Operators

ruby
# Simple assignment
x = 10
puts x            # 10

# Compound assignment operators
x += 5            # Equivalent to x = x + 5
puts x            # 15

x -= 3            # Equivalent to x = x - 3
puts x            # 12

x *= 2            # Equivalent to x = x * 2
puts x            # 24

x /= 4            # Equivalent to x = x / 4
puts x            # 6

x %= 5            # Equivalent to x = x % 5
puts x            # 1

x **= 3           # Equivalent to x = x ** 3
puts x            # 1

Parallel Assignment

ruby
# Basic parallel assignment
a, b = 1, 2
puts "a = #{a}, b = #{b}"  # a = 1, b = 2

# Swap variable values
a, b = b, a
puts "a = #{a}, b = #{b}"  # a = 2, b = 1

# Array destructuring
first, *rest = [1, 2, 3, 4, 5]
puts "first = #{first}"    # first = 1
puts "rest = #{rest}"      # rest = [2, 3, 4, 5]

# Ignore certain values
_, second, _ = [10, 20, 30]
puts "second = #{second}"  # second = 20

Conditional Assignment Operators

ruby
# ||= Conditional assignment (assign only if variable is nil or false)
name = nil
name ||= "Default Name"
puts name          # Default Name

name ||= "New Name"
puts name          # Default Name (won't change)

# &&= Conditional assignment (assign only if variable is truthy)
value = 10
value &&= value * 2
puts value         # 20

value = nil
value &&= value * 2
puts value         # nil

🔢 Bitwise Operators

Basic Bitwise Operators

ruby
# Bitwise AND (&)
puts 5 & 3         # 1 (101 & 011 = 001)

# Bitwise OR (|)
puts 5 | 3         # 7 (101 | 011 = 111)

# Bitwise XOR (^)
puts 5 ^ 3         # 6 (101 ^ 011 = 110)

# Bitwise NOT (~)
puts ~5            # -6 (two's complement)

# Left shift (<<)
puts 5 << 1        # 10 (101 << 1 = 1010)
puts 5 << 2        # 20 (101 << 2 = 10100)

# Right shift (>>)
puts 20 >> 1       # 10 (10100 >> 1 = 1010)
puts 20 >> 2       # 5 (10100 >> 2 = 101)

Bitwise Operations Application Example

ruby
# Permission check example
class Permissions
  READ = 1 << 0      # 1 (001)
  WRITE = 1 << 1     # 2 (010)
  EXECUTE = 1 << 2   # 4 (100)
  
  def initialize(permissions = 0)
    @permissions = permissions
  end
  
  def grant(permission)
    @permissions |= permission
  end
  
  def revoke(permission)
    @permissions &= ~permission
  end
  
  def has_permission?(permission)
    (@permissions & permission) != 0
  end
  
  def permissions
    perms = []
    perms << "Read" if has_permission?(READ)
    perms << "Write" if has_permission?(WRITE)
    perms << "Execute" if has_permission?(EXECUTE)
    perms.empty? ? "No permissions" : perms.join(", ")
  end
end

# Use permission system
user_perms = Permissions.new
puts user_perms.permissions  # No permissions

user_perms.grant(Permissions::READ)
user_perms.grant(Permissions::WRITE)
puts user_perms.permissions  # Read, Write

user_perms.revoke(Permissions::WRITE)
puts user_perms.permissions  # Read

📏 Range Operators

Range Creation

ruby
# Range including end value (..)
inclusive_range = 1..5
puts inclusive_range.include?(5)  # true
puts inclusive_range.to_a         # [1, 2, 3, 4, 5]

# Range excluding end value (...)
exclusive_range = 1...5
puts exclusive_range.include?(5)  # false
puts exclusive_range.to_a         # [1, 2, 3, 4]

# Character ranges
letter_range = 'a'..'e'
puts letter_range.to_a.join(',')  # a,b,c,d,e

Range Applications

ruby
# Using ranges in case statements
def grade(score)
  case score
  when 90..100
    "Excellent"
  when 80...90
    "Good"
  when 70...80
    "Average"
  when 60...70
    "Pass"
  else
    "Fail"
  end
end

puts grade(95)  # Excellent
puts grade(85)  # Good
puts grade(75)  # Average

# Range iteration
(1..5).each { |n| print "#{n} " }  # 1 2 3 4 5
puts

('a'..'e').each { |c| print "#{c} " }  # a b c d e
puts

🎯 Other Operators

Method Call Operators

ruby
# Dot operator (.)
class Calculator
  def add(a, b)
    a + b
  end
end

calc = Calculator.new
result = calc.add(2, 3)
puts result  # 5

# Safe navigation operator (&.)
user = nil
name_length = user&.name&.length  # Won't raise NoMethodError
puts name_length  # nil

Array and Hash Access Operators

ruby
# Array access
arr = [1, 2, 3, 4, 5]
puts arr[0]        # 1
puts arr[-1]       # 5
puts arr[1..3]     # [2, 3, 4]

# Hash access
hash = {name: "Zhang San", age: 25}
puts hash[:name]   # Zhang San
puts hash["name"]  # nil (key doesn't exist)

# Default value access
scores = Hash.new(0)
scores[:math] = 95
puts scores[:english]  # 0 (default value)

Regular Expression Operators

ruby
# Match operator (=~)
text = "Hello, Ruby!"
puts text =~ /Ruby/    # 7 (match position)
puts text =~ /python/  # nil (no match)

# Non-match operator (!~)
puts text !~ /Ruby/    # false
puts text !~ /python/  # true

# Access match data
if match_data = "2023-12-25".match(/(\d{4})-(\d{2})-(\d{2})/)
  year, month, day = match_data[1], match_data[2], match_data[3]
  puts "Year: #{year}, Month: #{month}, Day: #{day}"  # Year: 2023, Month: 12, Day: 25
end

⚖️ Operator Precedence

Precedence List (from High to Low)

ruby
# 1. ! ~ + - (unary operators)
# 2. ** (exponentiation)
# 3. * / % (multiplication, division, modulo)
# 4. + - (addition, subtraction)
# 5. << >> (bit shift)
# 6. & (bitwise AND)
# 7. | ^ (bitwise OR, XOR)
# 8. > >= < <= (comparison)
# 9. <=> == === != =~ !~ (equality)
# 10. && (logical AND)
# 11. || (logical OR)
# 12. .. ... (range)
# 13. ? : (ternary operator)
# 14. = += -= *= /= %= **= &= |= ^= <<= >>= (assignment)
# 15. not (logical NOT)
# 16. and or (logical operations)

# Example: Understanding precedence
result = 2 + 3 * 4     # 14 (multiplication has higher precedence than addition)
result = (2 + 3) * 4   # 20 (parentheses change precedence)

result = true || false && false  # true (&& has higher precedence than ||)
result = true or false and false # false (and/or have lower precedence than ||)

Use Parentheses to Clarify Intent

ruby
# Good practice: Use parentheses to clarify operation order
average = (score1 + score2 + score3) / 3
is_valid = (user.active? && user.verified?) || admin?
result = (base_value * multiplier) + adjustment

# Avoid relying on memorizing operator precedence
# bad: is_adult = age >= 18 && has_id && !is_banned
# good: is_adult = (age >= 18) && has_id && (!is_banned)

🧪 Operator Overloading

Custom Operators

ruby
class Vector
  attr_reader :x, :y
  
  def initialize(x, y)
    @x = x
    @y = y
  end
  
  # Overload addition operator
  def +(other)
    Vector.new(@x + other.x, @y + other.y)
  end
  
  # Overload subtraction operator
  def -(other)
    Vector.new(@x - other.x, @y - other.y)
  end
  
  # Overload multiplication operator
  def *(scalar)
    Vector.new(@x * scalar, @y * scalar)
  end
  
  # Overload equality operator
  def ==(other)
    other.is_a?(Vector) && @x == other.x && @y == other.y
  end
  
  # Overload string conversion operator
  def to_s
    "(#{@x}, #{@y})"
  end
end

# Use custom operators
v1 = Vector.new(1, 2)
v2 = Vector.new(3, 4)

v3 = v1 + v2
puts v3  # (4, 6)

v4 = v2 - v1
puts v4  # (2, 2)

v5 = v1 * 3
puts v5  # (3, 6)

puts v1 == Vector.new(1, 2)  # true

🎯 Operator Best Practices

1. Choose Appropriate Operators

ruby
# Good practice: Use intuitive operators
if user.age >= 18
  puts "Adult"
end

# Avoid complex operator combinations
# bad: result = a && b || c && d
# good: 
has_permission = a && b
has_exception = c && d
result = has_permission || has_exception

2. Use Short-Circuit Evaluation Reasonably

ruby
# Safe conditional checks
def process_user(user)
  # Use short-circuit evaluation to avoid nil errors
  if user && user.active? && user.profile && user.profile.complete?
    # Process user
  end
end

# Use safe navigation operator (Ruby 2.3+)
def process_user_modern(user)
  if user&.active? && user&.profile&.complete?
    # Process user
  end
end

3. Use Operator Overloading Carefully

ruby
class Money
  attr_reader :amount, :currency
  
  def initialize(amount, currency = "USD")
    @amount = amount
    @currency = currency
  end
  
  # Only overload meaningful operators
  def +(other)
    if other.currency == @currency
      Money.new(@amount + other.amount, @currency)
    else
      # Currency conversion logic
      raise "Currency mismatch"
    end
  end
  
  # Provide clear comparison methods
  def >(other)
    @amount > other.amount
  end
  
  def <(other)
    @amount < other.amount
  end
  
  def ==(other)
    other.is_a?(Money) && @amount == other.amount && @currency == @currency
  end
  
  def to_s
    "#{@amount} #{@currency}"
  end
end

# Use money class
price1 = Money.new(100, "USD")
price2 = Money.new(50, "USD")
total = price1 + price2
puts total  # 150 USD

🧪 Practice Examples

Mathematical Expression Calculator

ruby
class ExpressionCalculator
  def self.evaluate(expression)
    # Simple expression parsing (example only)
    # In practice, use a more complex parser
    
    # Remove spaces
    expression = expression.gsub(/\s+/, "")
    
    # Process multiplication and division
    while expression.match?(/[\*\/]/)
      expression.sub!(/(\d+(?:\.\d+)?)\*([+-]?\d+(?:\.\d+)?)/) do |match|
        $1.to_f * $2.to_f
      end
      
      expression.sub!(/(\d+(?:\.\d+)?)\/([+-]?\d+(?:\.\d+)?)/) do |match|
        $1.to_f / $2.to_f
      end
    end
    
    # Process addition and subtraction
    while expression.match?(/[+-]/)
      expression.sub!(/([+-]?\d+(?:\.\d+)?)\+([+-]?\d+(?:\.\d+)?)/) do |match|
        $1.to_f + $2.to_f
      end
      
      expression.sub!(/([+-]?\d+(?:\.\d+)?)\-([+-]?\d+(?:\.\d+)?)/) do |match|
        $1.to_f - $2.to_f
      end
    end
    
    expression.to_f
  end
end

# Use calculator
puts ExpressionCalculator.evaluate("2 + 3 * 4")     # 14.0
puts ExpressionCalculator.evaluate("10 / 2 - 3")    # 2.0
puts ExpressionCalculator.evaluate("2.5 * 4 + 1")   # 11.0

Permission Management System

ruby
class PermissionSystem
  # Implement permission management using bitwise operations
  PERMISSIONS = {
    read: 1 << 0,      # 1
    write: 1 << 1,     # 2
    execute: 1 << 2,   # 4
    delete: 1 << 3,    # 8
    admin: 1 << 4      # 16
  }.freeze
  
  def initialize(initial_permissions = 0)
    @permissions = initial_permissions
  end
  
  def grant(*permissions)
    permissions.each do |perm|
      if PERMISSIONS.key?(perm)
        @permissions |= PERMISSIONS[perm]
      else
        raise ArgumentError, "Unknown permission: #{perm}"
      end
    end
    self
  end
  
  def revoke(*permissions)
    permissions.each do |perm|
      if PERMISSIONS.key?(perm)
        @permissions &= ~PERMISSIONS[perm]
      end
    end
    self
  end
  
  def has_permission?(permission)
    PERMISSIONS.key?(permission) && (@permissions & PERMISSIONS[permission]) != 0
  end
  
  def can_read?
    has_permission?(:read)
  end
  
  def can_write?
    has_permission?(:write)
  end
  
  def can_execute?
    has_permission?(:execute)
  end
  
  def is_admin?
    has_permission?(:admin)
  end
  
  def permissions_list
    PERMISSIONS.select { |name, value| (@permissions & value) != 0 }.keys
  end
  
  def to_s
    permissions_list.empty? ? "No permissions" : permissions_list.join(", ")
  end
end

# Use permission system
user = PermissionSystem.new
puts user  # No permissions

user.grant(:read, :write)
puts user  # read, write
puts user.can_read?    # true
puts user.can_execute? # false

user.grant(:admin)
puts user  # read, write, admin
puts user.is_admin?    # true

user.revoke(:write)
puts user  # read, admin

📚 Next Steps

After mastering Ruby operators, it is recommended to continue learning:

Continue your Ruby learning journey!

Content is for learning and research only.