Ruby - Spaceship运算符不能在.sort

时间:2015-06-29 14:18:23

标签: ruby sorting comparison

尝试在sort函数中使用非字母数字字符的太空船操作符时出错。

word = "out-classed"
letters = word.downcase.split('')
letters.sort! do |x, y|
  if y < 'a'
    next
  else
   value = x <=> y
  end
end

我得到了ArgumentError: comparison of String with String failed,而且我几乎肯定这是太空船运营商而不是&lt;比较。

有趣的是,当我在排序块的上下文之外的irb中进行相同的比较时,比较起作用。当word变量只包含字母时,它也有效。

任何人都可以帮助我理解为什么这个在这个特定的背景下不起作用吗?

3 个答案:

答案 0 :(得分:3)

如果您尝试对集合进行排序,x<=>y必须为集合的每对元素返回0,1或-1。如果<=>是针对某些对(例如'a'<=>'-' #=> 0'-'<=>'a' #=> 0)人为定义的,则您的排序可能会返回错误的结果。

这是因为排序算法不一定要评估集合中的所有元素对。例如,如果它发现:

'a' <=> 'b' #=> 0

'b' <=> 'c' #=> 0

它将得出结论:

`a` <=> `c` #=> 0

因为正在排序的集合必须满足传递性x <== z x <= yy <= z

例如,如果集合是数组['z', '-', 'a']并且它找到'z' <= '-''-' <= 'a',则会得出'z' <= 'a'(并且不评估'z' <=> 'a' })。

这就是原因:

['z', '-', 'a'].sort { |x,y| p [x,y]; (y < 'a') ? 0 : x<=>y }
  #-> ["z", "-"]
  #-> ["-", "a"]
  #=> ["z", "-", "a"]

不起作用。你有两个选择:

在排序前删除有问题的元素:

['z', '-', 'a'].select { |c| ('a'..'z').cover?(c) }.
                sort { |x,y| (y < 'a') ? 0 : x<=>y }
  #=> ["a", "z"]

或对集合的所有元素进行排序:

['z', '-', 'a'].sort
  #=> ["-", "a", "z"] 

如果集合包含不可比较的元素(例如[1,2,'cat']),您只能选择从数组中删除元素,直到所有剩余元素都具有可比性。

答案 1 :(得分:2)

而不是next,你需要返回0,1或-1。试试这个:

word = "out-classed"
letters = word.downcase.split('')
letters.sort! do |x, y|
  if y < 'a'
    0
  else
   value = x <=> y
  end
end

答案 2 :(得分:1)

你的问题就在这里

if y < 'a'
  next
else
  ...

sort方法希望您在每个对之间返回一个比较值,因此当您调用next而不返回任何内容时,它会说比较失败。

尝试例如这样:

if y < 'a'
  1
else
  value = x <=> y
end