Почему uniq! return nil, если нет дубликатов
Я только начинаю с Ruby, и я лично считаю следующее нарушением "принципа наименьшего удивления". И это, цитируя документацию, что uniq! "удаляет повторяющиеся элементы из себя. Возвращает nil, если никаких изменений не производится (то есть, не найдены дубликаты).
Может кто-нибудь объяснить это, что кажется мне совершенно интуитивным? Это означает, что вместо того, чтобы писать одну строку кода ниже, добавив .uniq! для завершения первой строки я должен написать следующие две строки:
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks = hooks.uniq
Или я что-то упускаю, лучший способ?
EDIT:
Я понимаю, что uniq! изменяет его операнд. Здесь лучше иллюстрируется проблема, я надеюсь:
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
puts hooks.length #50
puts hooks.uniq!.length #undefined method `length' for nil:NilClass
Я утверждаю, что путь uniq! работы делают его совершенно бессмысленным и бесполезным. Конечно, в моем случае, как указано, я мог просто добавить .uniq в первую строку. Однако позже в той же программе я нажимаю элементы на другой массив внутри цикла. Затем, под циклом, я хотел бы "де-обмануть" массив, но я не смею писать "hooks_tested.uniq!" потому что он мог бы вернуть нуль; вместо этого я должен написать hooks_tested = hooks_tested.uniq
Действительно, я утверждаю, что это особенно вопиющая неправильная особенность в том, что хорошо известный принцип заключается в том, что при разработке метода, который возвращает массив, всегда нужно как минимум возвращать пустой массив, а не nil
Ответы
Ответ 1
Это связано с тем, что uniq!
изменяет self
, и если uniq!
вернет значение, вы не сможете узнать, действительно ли произошло изменение исходного объекта.
var = %w(green green yellow)
if var.uniq!
# the array contained duplicate entries
else
# nothing changed
end
В коде вы можете просто написать
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq!
# here hooks is already changed
Если вам нужно вернуть значение hook, возможно, потому что это последний оператор метода просто
def method
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq
end
или иначе
def method
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq!
hooks
end
Ответ 2
Восклицательный знак на uniq!
указывает, что он изменяет массив вместо того, чтобы возвращать новый. Вы должны сделать это:
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).uniq
или
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)
hooks.uniq!
puts hooks.length
Ответ 3
Вы можете добавить uniq
(восклицательный знак в конце) в конец первой строки.
Или, если вы настаиваете на использовании uniq!
, используйте
(hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)).uniq!
Ответ 4
Так как Ruby 1.9, Object#tap
доступен:
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap do |hooks|
hooks.uniq!
end
puts hooks.length
И, возможно, более кратко (h/t @Aetherus):
hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap(&:uniq!)
puts hooks.length
Ответ 5
Это не ответ на вопрос, а скорее обходной путь.
Так как uniq
не возвращает nil
, я использую uniq
и присваиваю результат новой переменной вместо использования версии bang
original = [1,2,3,4]
new = original.uniq
#=> new is [1,2,3,4]
#=> ... rather than nil
Наличие новой переменной - небольшая цена для оплаты. Конечно, черт побери, если чеки, с повторными сложными вызовами на uniq!
и uniq
и проверка на nil