Ruby - Spaceship 运算符不会在 .sort 的块中工作

Ruby - Spaceship operator won't work in a block for .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,而且我几乎肯定这发生在宇宙飞船操作员身上,而不是 < 比较。

有趣的是,当我在排序块上下文之外的 irb 中进行相同的比较时,比较有效。它也适用于单词变量仅由字母组成的情况。

谁能帮我理解为什么这在这个特定的上下文中不起作用?

你的问题就在这里

if y < 'a'
  next
else
  ...

sort 方法期望您 return 比较 每对 之间的值,因此当您调用 next 时没有 return 任何东西,它说比较失败。

尝试例如这个:

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

您需要 return0、1 或 -1,而不是 next。试试这个:

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

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

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

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

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

它将得出结论:

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

因为被排序的集合必须满足传递性: x <== z if x <= y and y <= z.

例如,如果集合是数组['z', '-', 'a'],它发现'z' <= '-''-' <= 'a',它会得出[​​=26=](而不是评估'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']),您唯一的选择是从数组中删除元素,直到所有剩余元素都可比较。