尝试在排序函数中使用带有非字母数字字符的宇宙飞船运算符时出现错误。
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
,我几乎可以肯定这发生在宇宙飞船运算符上,而不是 < 比较。
有趣的是,当我在排序 block 上下文之外的 irb 中执行相同的比较时,比较有效。它也适用于单词变量仅由字母组成的情况。
谁能帮我理解为什么这在这个特定的上下文中不起作用?
最佳答案
如果您尝试对集合进行排序,x<=>y
必须为集合中的每对元素返回 0、1 或 -1。如果<=>
为某些对(例如 'a'<=>'-' #=> 0
和 '-'<=>'a' #=> 0
)人为定义,您的排序可能会返回错误的结果。
这是因为排序算法不一定评估集合中的所有元素对。例如,如果它发现:
'a' <=> 'b' #=> 0
和
'b' <=> 'c' #=> 0
它将得出结论:
`a` <=> `c` #=> 0
因为被排序的集合必须满足传递性:x <== z
如果x <= y
和 y <= 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']
),您唯一的选择是从数组中删除元素,直到所有剩余元素都可比较为止。
关于Ruby - Spaceship 运算符不会在 .sort 的 block 中工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31118077/