Ruby: Сортировка многомерного массива, который может содержать nil, иногда терпит неудачу
Я нашел этот пример в неудачном тесте в одном из моих проектов. Почему это работает:
[[1,2,3], [2,3,4], [1,1,nil]].sort
#=> [[1, 1, nil], [1, 2, 3], [2, 3, 4]]
Но это не так:
[[1,2,3], [nil,3,4], [1,1,nil]].sort
#=> ERROR: ArgumentError: comparison of Array with Array failed
Протестированные версии Ruby: 2.0.0
, 1.9.3
.
Ответы
Ответ 1
Он не работает, потому что он перебирает nil
. Причина, по которой первый тестовый пример не сбой, заключается в том, что сравнение 1, 1
происходит с 1, 2
. Не проверять nil
, поскольку это не нужно.
Ниже приведен пример ниже, потому что он должен сначала пройти nil
. Попробуйте в irb:
[1, nil, 2].sort
ArgumentError: comparison of Fixnum with nil failed
# in this example 1,2 comes before 2,1 so no need to continue in sort
# and nil is never reached
[ [2,1,3,5], [1,2,nil,6] ].sort
=> [[1, 2, nil, 6], [2, 1, 3, 5]]
Ответ 2
Это из-за сравнения между массивами для их сортировки, это элемент для элемента:
В первом случае [[1,2,3], [2,3,4], [1,1,nil]].sort
алгоритм сравнивает:
1 (of the first array) < 2 (of the second array)
1 (of the first array) = 1 (of the third array)
Тогда, поскольку существуют два элемента, мы должны сравнить второй элемент:
2 (of the first array) > 1 (of the third array)
Затем сравнение заканчивается, нет необходимости сравнивать 3-й элемент.
Теперь, во втором случае [[1,2,3], [nil,3,4], [1,1,nil]].sort
, сравнение первого элемента массивов содержит nil, и это вызовет ошибку.
Например, это вызовет ошибку:
[[1,2,3], [1,nil,4], [3,1,3]].sort
Это не будет:
[[2,2,3], [1,nil,4], [3,1,3]].sort
Ответ 3
Это связано с тем, что метод #sort
использует методы сортировки QSort, и в следующих случаях процедура сортировки достигает не последнего значения массива. В вашем случае [1,1,nil]
это nil
. И он достигает nil
в случае 1,nil,4
, потому что в последнем случае метод #<=>
возвращает nil
. Чтобы избежать ошибки, вам нужно переопределить метод Array
#<=>
или использовать сложный блок:
[[1,2,3], [nil,3,4], [1,1,nil]].sort do| x, y |
x <=> y || 1
end