Search code examples
rubyalgorithmloopsinfinite-loopbubble-sort

Ruby infinite loop in bubble sort algorithm


I have build the basic bubble sort algorithm with Ruby, without problems. The code is as follows:

def bubble_sort(arr)
 swapped=true
 while swapped
    swapped=false
    for j in 0..arr.length-2 do
      if arr[j]>arr[j+1]
        arr[j], arr[j+1] = arr[j+1], arr[j]
        swapped=true
      end
    end
 end
arr
end

Now, I am trying to implement the same method, but with a function of accepting code block. Code block part works fine, but when code block is not provided, the method should work like above, although it looks logically same to me, but for some reason, it goes into an infinite loop:

On the line of "unless", it will check for the condition and swap positions if necessary, and will skip the yield part. I tried step by step debugging by rdebugger but could not find out the reason.

def bubble_sort_by(arr)
  swapped = true
  while swapped
    swapped=false
    for i in 0..arr.length-2 do
      unless block_given?
        arr[i], arr[i+1] = arr[i+1], arr[i] if arr[i] < arr[i+1]
        swapped=true
      end #unless
    if block_given?
      if yield(arr[i], arr[i+1])>0
        arr[i], arr[i+1] = arr[i+1], arr[i]
        swapped=true
      end #if yield
    end #if block_given?
    end #for
  end #while
puts arr
return arr
end

Solution

  • Quick answer:

    arr[i], arr[i+1] = arr[i+1], arr[i] if arr[i] < arr[i+1]
    swapped=true
    

    Should be replaced with:

    if arr[i] < arr[i+1]
      arr[i], arr[i+1] = arr[i+1], arr[i]
      swapped=true
    end
    

    What's happening is you're always setting swapped to true, even if the elements were not swapped. So you get stuck in an infinite loop.

    And now for a bit of code clean-up... First, rather than writing:

    if(foo)
      # ...
    end
    unless(foo)
      # ...
    end
    

    Let's make it an if/else statement:

    def bubble_sort_by(arr) 
      swapped = true 
    
      while swapped 
        swapped=false 
        for i in 0..arr.length-2 do 
          if block_given? 
            if yield(arr[i], arr[i+1])>0 
              arr[i], arr[i+1] = arr[i+1], arr[i] 
              swapped=true 
            end 
          else 
            if arr[i] < arr[i+1] 
              arr[i], arr[i+1] = arr[i+1], arr[i] 
              swapped=true 
            end 
          end 
        end #for 
      end #while 
    
      return arr 
    end
    

    You could re-factor it further to remove the while loop as @Aetherus suggests, but I figured you'd appreciate seeing the actual bug fixed.