如何在 Ruby 中递归传递未知 Proc

How to pass unknown Proc recursively in Ruby

问题

我正在尝试编写一个方法 (Array#bubble_sort),它接受一个可选的 proc 参数。
当给定过程时,该方法应根据过程对数组进行排序。
当没有给出 proc 时,该方法应按递增顺序对数组进行排序。
递归地,这在不添加 proc 的情况下工作正常,但是当给定一个未知的 proc 时,我找不到将它作为参数正确传递回来的方法。

代码

class Array
  def bubble_sort(&prc)

    prc = Proc.new{|a,b| a <=> b} if !prc

    self.each_with_index do |ele,idx|
      if prc.call(self[idx],self[idx+1]) == 1
        self[idx],self[idx+1] = self[idx+1], self[idx]
        self.bubble_sort(&prc) 
      end
    end

    self
    end
end

测试代码

[4, 12, 2, 8, 1, 14, 9, 25, 24, 81].bubble_sortreturns[1, 2, 4, 8, 9, 12, 14, 24, 25, 81]

的预期结果

[4, 12, 2, 8, 1, 14, 9, 25, 24, 81].bubble_sort { |a, b| a.to_s <=> b.to_s } returns 堆栈级别太深错误而不是 [1, 12, 14, 2, 24, 25, 4, 8, 81, 9]

的预期结果

问题不在于您传递过程,而是 <=> 在数字和字符串上的工作方式不同。

当循环到达数组末尾时,self.idx 等于 self.length - 1self.idx + 1 等于 self.length。由于零索引,在数组上调用 self[self.length] 将始终为 nil。

所以,你最终调用了 proc.call(<last element of array>, nil)

宇宙飞船操作员的行为会有所不同,具体取决于您是 number <=> nil 还是 number.to_s <=> nil.to_s(这是您比较的两个过程之间的区别):

81 <=> nil
# => nil

81.to_s <=> nil.to_s
# => 1

在你的例子中,你不希望与 nil 的任何比较导致交换,所以你有两个选择:

  1. 如果第二个元素为nil,可以将proc改为return nil:

    arr.bubble_sort do |a,b|
      b.nil? ? nil : a.to_s <=> b.to_s
    end
    
  2. 如果 b 超出范围,您可以完全跳过比较:

    self.each_with_index do |ele,idx|
      next if idx + 1 == self.length
      # ... other stuff