如何将块传递给我的"冒泡排序"方法?

时间:2016-04-30 20:31:48

标签: ruby yield bubble-sort

以下代码是我对冒泡排序方法的新手。

#For each element in the list, look at that element and the element
#directly to it's right. Swap these two elements so they are in 
#ascending order.

def bubble_sort (array)
    a = 0
    b = 1
    until (array.each_cons(2).all? { |a, b| (a <=> b) <= 0}) == true do
        sort = lambda {array[a] <=> array[b]}
        sort_call = sort.call
        loop do
            case sort_call
            when -1 #don't swap
                a += 1
                b += 1
                break
            when 0 #don't swap
                a += 1
                b += 1
                break
            when 1 #swap
                array.insert(a,array.delete_at(b))
                a += 1
                b += 1
                break
            else #end of array, return to start
                a = 0
                b = 1
                break
            end
        end
    end
    puts array.inspect
end

array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
bubble_sort(array)

我希望能够改变这个方法,以便它将一段代码作为参数并使用它来确定它的排序方式。

例如: array = ["hello", "my", "name", "is", "daniel"] bubble_sort(array) {array[@a].length <=> array[@b].length}

(当我尝试过此操作时,我已将ab转换为整个代码中的实例变量。)

我尝试过使用yield但是一旦到达数组末尾就得到undefined method 'length' for nil:NilClass。我已尝试添加诸如

之类的内容
if array[@b+1] == nil
    @a = 0
    @b = 1
end

这有帮助,但我仍然会遇到像无限循环这样的奇怪问题,或者无法排序超过一定数量的元素。

长话短说,我已经在这几个小时了。有没有一种简单的方法可以做我想做的事情?感谢。

2 个答案:

答案 0 :(得分:1)

你离得太远了。只是一些事情:

  1. 让你的函数采用块参数

    def bubble_sort (array, &block)
    
  2. 检查用户是否提供了一个块

    if block_given?
        # Call user's comparator block
    else
        # Use the default behavior
    end
    
  3. 调用用户的比较器块

    block.call(a, b)
    
  4. 在用户提供的块中,接受要比较的元素的块参数

    bubble_sort(array) {|a,b| a.length <=> b.length}
    
  5. 这应该会让你进入正确的球场。

答案 1 :(得分:1)

你打电话给你的lambda的方式有点奇怪。这实际上是完全没必要的。我重构了你的代码并清理了一些冗余。以下适用于我:

def sorted?(arr)
  arr.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end

def bubble_sort (arr)
  a = 0
  b = 1
  until sorted?(arr) do
    # The yield call here passes `arr[a]` and `arr[b]` to the block.
    comparison = if block_given? 
               yield(arr[a], arr[b])
             else
               arr[a] <=> arr[b]
             end

    if [-1, 0, 1].include? comparison
      arr.insert(a, arr.delete_at(b)) if comparison == 1

      a += 1
      b += 1
    else
      a = 0
      b = 1
    end
  end

  arr
end

sample_array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]

# Sanity check:
100.times do
  # `a` is the value of `arr[a]` in our function above. Likewise for `b` and `arr[b]`.
  print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
end

修改

清洁版:

  # In place swap will be more efficient as it doesn't need to modify the size of the arra
def swap(arr, idx)
  raise IndexError.new("Index #{idx} is out of bounds") if idx >= arr.length || idx < 0

  temp         = arr[idx]
  arr[idx]     = arr[idx + 1]
  arr[idx + 1] = temp
end

def bubble_sort(arr)
  loop do
    sorted_elements = 0

    arr.each_cons(2).each_with_index do |pair, idx|
      comparison = if block_given?
                     yield pair.first, pair.last
                   else
                     pair.first <=> pair.last
                   end

      if comparison > 0
        swap(arr, idx)
      else
        sorted_elements += 1
      end
    end

    return arr if sorted_elements >= arr.length - 1
  end
end

# A simple test

sample_array     = [4, 2, 2, 2, 2, 2, 5, 5, 6, 3, 23, 5546, 234, 234, 6]
sample_str_array = ["a", "ccc", "ccccc"]

100.times do
  print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
  print bubble_sort(sample_str_array.shuffle) { |a, b| a.length <=> b.length }, "\n"
end