宇宙飞船如何< => ruby中的运算符抛出异常?

时间:2013-08-20 23:07:54

标签: ruby exception

我有以下代码:

def sort_descending _objects, field
  _objects.sort { |b,a| a.send(field) <=> b.send(field) }
end

a.send(field)返回Stringb.send(field)返回Integer时,则会引发ArgumentError。我试图通过以下方式捕获此异常:

def sort_descending _objects, field
  _objects.sort { |b,a| safe_compare(a,b,field) }
end

def safe_compare a, b, field
   a.send(field) <=> b.send(field)
rescue
   a.send(field).to_s <=> b.send(field).to_s
end

但这也会引发ArgumentError。我不知道为什么。任何人都可以解释sort抛出的异常行为吗?

虽然这种解决方法有效但看起来很难看

def sort_ascending _objects, field
  _objects.sort do |a,b|
    safe_compare(a,field,b) <=> safe_compare(b,field,a)
  end
end

def safe_compare a, field, b
  _a,_b = a.send(field), b.send(field)
  _a.class == _b.class ? _a : _a.to_s
end

要重现的代码是here

1 个答案:

答案 0 :(得分:4)

  

有人可以解释一下吗?

是的,方法&lt; =&gt;()不会引发异常。看看:

def sort_descending _objects, field
  _objects.sort {|b,a| safe_compare(a,b,field) }
end

def safe_compare a, b, field
  a.send(field) <=> b.send(field)
rescue 
  puts 'in rescue clause'   #Let's trace the flow of execution
  a.send(field).to_s <=> b.send(field).to_s
end

class Dog
  def greet
    "hello"
  end
end

class Cat
  def greet
    10
  end
end

d = Dog.new
c = Cat.new

p d.send("greet")
p c.send("greet")

p safe_compare(d, c, "greet")

--output:--
"hello"
10
nil

请注意,救援条款中的puts语句没有输出。

来自ruby String docs:

string <=> other_string → -1, 0, +1 or nil

nil is returned if the two values are incomparable.

这一行:

a.send(field) <=> b.send(field)

相当于:

a.send(field).<=>( b.send(field) )

如果a.send(field)返回一个字符串,那么一个字符串正在调用&lt; =&gt;()方法。 Numeric类还定义了&lt; =&gt;()方法,因此如果a.send(field)返回一个数字,则数字会调用&lt; =&gt;()方法。字符串#&lt; =&gt;和数字#&lt; =&gt;如果两个对象不具有可比性,则返回nil - 它们不会抛出异常。其他类具有类似的&lt; =&gt;()方法的定义。

因此,在safe_compare方法中不会引发任何ArgumentError。但是,nil不是排序块的有效返回值,因此sort()会引发ArgumentError。

你需要做这样的事情:

def sort_descending _objects, field
  _objects.sort { |b,a| safe_compare a, b, field }
end

def safe_compare a, b, field
  result = a.send(field) <=> b.send(field)
  result ||= a.send(field).to_s <=> b.send(field).to_s
end