Chapter 05 - Unlock the Mystical Powers of Stacks and Conjure up Magic in Ruby

Conjure Code Magic: Orchestrating Enchanted Data Stacks with Ruby’s Warm Breakfast Charm

Chapter 05 - Unlock the Mystical Powers of Stacks and Conjure up Magic in Ruby

Stacks are like those mystical boxes we keep stuffing things into and only pull out whatever’s on top. A trusty sidekick in many programming escapades, stacks follow the Last In, First Out (LIFO) principle. Imagine it like a stack of plates; you can only take the top plate off or put another one on top. Let’s dive deeper into this concept and see how we can implement it using Ruby, the language that feels like a warm Sunday morning breakfast.

To start with, a stack performs several key operations. We have the push operation, where we add an item to the top of the stack. There’s the pop operation, which removes the top item, and then there’s peek, which lets us see the top item without removing it. Implementing a stack can be as simple or complex as you want it to be, depending on whether you use arrays or linked lists.

Let’s play around with stacks in Ruby using arrays, which are inherently stack-friendly. Imagine we have an array named my_stack, and it’s here that we conjure up our magical stack operations.

class StackUsingArray
  def initialize
    @stack = []
  end

  def push(element)
    @stack.push(element)
  end

  def pop
    @stack.pop
  end

  def peek
    @stack.last
  end

  def show
    @stack
  end
end

stack = StackUsingArray.new
stack.push(1)
stack.push(2)
stack.push(3)
puts stack.peek # Outputs 3
stack.pop
puts stack.show # Outputs [1, 2]

This feels like adding and removing stories from a skyscraper, right? However, some might say stacks using arrays are a bit pedestrian. Let’s fan up some enigmatic flair by using linked lists. Each element in this stack is a node with a reference to the next one, like a caravan of mystical adventurers.

class Node
  attr_accessor :value, :next_node

  def initialize(value)
    @value = value
    @next_node = nil
  end
end

class StackUsingLinkedList
  def initialize
    @top = nil
  end

  def push(element)
    new_node = Node.new(element)
    new_node.next_node = @top
    @top = new_node
  end

  def pop
    return nil if @top.nil?
    
    popped = @top.value
    @top = @top.next_node
    popped
  end

  def peek
    @top&.value
  end

  def show
    node = @top
    elements = []
    while node
      elements << node.value
      node = node.next_node
    end
    elements
  end
end

linked_list_stack = StackUsingLinkedList.new
linked_list_stack.push('a')
linked_list_stack.push('b')
linked_list_stack.push('c')
puts linked_list_stack.peek # Outputs 'c'
linked_list_stack.pop
puts linked_list_stack.show # Outputs ['b', 'a']

In this mystical world of stacks, each node carries not just its value but a memory of the next, maintaining a chain of command until the very last element. Now, where does one use such mystical swords in battle?

Stacks aren’t just theoretical playthings; they sneak into real-world applications like expression evaluation. Think about parsing a math expression with a bunch of parentheses and operators, quite like unscrambling a wizard’s spellbook. A stack can manage the operators and parentheses while traversing the expression, popping and pushing as needed.

def evaluate_postfix(expression)
  stack = []

  expression.split.each do |token|
    case token
    when /\d/
      stack.push(token.to_i)
    when '+', '-', '*', '/'
      b = stack.pop
      a = stack.pop
      result = a.send(token, b)
      stack.push(result)
    end
  end

  stack.pop
end

puts evaluate_postfix("5 1 2 + 4 * + 3 -") # Outputs 14

Here’s a neat trick, stacks are also best mates with recursion. Recursion can fundamentally be replaced by a stack to track function calls. Every recursive call can transform into pushing a new frame onto a call stack. But why travel so far? Instead, envision how browsers use stacks to manage backward and forward history. Each webpage visited is a push; each back navigation is a pop.

Stacks subtly orchestrate the dance of data, quietly managing lifecycles from games to compilers. It’s about orchestrating operations like a puppeteer directing the performance from above. Whether you embrace arrays or prefer the mysterious charm of linked lists, stacks will always have your back, providing a robust and reliable data structure—an ally in the ever-complex battles of programming.

Now that you’ve entered the enchanting world of stacks, why not use them to conjure up some magic in your code? Dive into it with creativity and curiosity, and may your programming journey be ever thrilling and intriguing!