This blog post delves into the world of code optimization, offering practical strategies to enhance the performance of your code. We'll explore how to choose the right data structures for the task, minimize unnecessary loops and calculations, leverage caching techniques effectively, and write clean, DRY (Don't Repeat Yourself) code. We'll also delve into the importance of considering time and space complexity when crafting your code.
Choose the right data structure for your task.
Example: Arrays are fast for indexing, while hashes are better for key-value lookups.
# Inefficient
names = ["Alice", "Bob", "Charlie"]
names.include?("Bob") # Searches through entire array
# Optimized
names_set = Set.new(["Alice", "Bob", "Charlie"])
names_set.include?("Bob") # Faster lookup
Reduce the number of times you loop through data.
# Inefficient: Multiple loops through the same data
numbers = [1, 2, 3, 4, 5]
sum = 0
product = 1
max = numbers[0]
min = numbers[0]
numbers.each { |n| sum += n }
numbers.each { |n| product *= n }
numbers.each { |n| max = n if n > max }
numbers.each { |n| min = n if n < min }
# Optimized: Single loop to calculate all values
numbers = [1, 2, 3, 4, 5]
sum = 0
product = 1
max = numbers[0]
min = numbers[0]
numbers.each do |n|
sum += n
product *= n
max = n if n > max
min = n if n < min
end
Don't repeat calculations that don't change.
Example: storing the result of a calculation that doesn't change, and then reusing that stored result instead of recalculating it.
class ExpensiveCalculation
def initialize
@result = nil
end
def calculate
@result ||= perform_expensive_calculation
end
private
def perform_expensive_calculation
puts "Performing expensive calculation..."
# Simulating an expensive calculation
sleep(2)
42
end
end
calculation = ExpensiveCalculation.new
# First call, performs the calculation
puts calculation.calculate # => Performing expensive calculation... 42
# Second call, uses the cached result
puts calculation.calculate # => 42
Store results of expensive operations for reuse.
The Fibonacci sequence, when implemented recursively, often leads to redundant calculations. For instance, the standard recursive approach calculates the same Fibonacci numbers multiple times. We can avoid this inefficiency by using alternative methods like an iterative approach or tail recursion. These techniques allow us to store intermediate results in variables, eliminating the need for repeated calculations. This principle of storing results instead of recalculating them can be applied to many other scenarios to improve code efficiency. For more information, visit https://vulehuan.com/en/blog/2024/7/unraveling-recursion-understanding-the-magic-behind-it-66951167cdb8828661f4c415.html
Web caching ideas:
Order conditions efficiently and use case when appropriate.
# Inefficient
if x == 1
puts "One"
elsif x == 2
puts "Two"
elsif x == 3
puts "Three"
end
# Optimized
case x
when 1 then puts "One"
when 2 then puts "Two"
when 3 then puts "Three"
end
Example: Ruby has many efficient built-in methods. Use them!
# Inefficient
words = text.split(" ")
word_count = {}
words.each do |word|
word_count[word] ||= 0
word_count[word] += 1
end
# Optimized
word_count = text.split.tally
Don't repeat yourself. Consolidate repeated code into functions.
Have you heard of DRY (Don't Repeat Yourself)?? It avoids code duplication, promoting cleaner and more maintainable code. For more information, visit https://vulehuan.com/en/blog/2024/7/write-cleaner-maintainable-code-with-dry-kiss-solid-principles-6688ca8bc9dec2dec37a1b94.html
Think about how your code scales with larger inputs.
# O(n^2) time complexity
def has_duplicate?(array)
array.each_with_index do |item, i|
array[(i+1)..-1].each do |other|
return true if item == other
end
end
false
end
# O(n) time complexity, O(n) space complexity
def has_duplicate?(array)
seen = Set.new
array.each do |item|
return true if seen.include?(item)
seen.add(item)
end
false
end