Почему Uniq! вернуть ноль, если нет дубликатов - PullRequest
11 голосов
/ 20 января 2010

Я только начинаю с Ruby, и лично я считаю, что следующее является нарушением "принципа наименьшего удивления". То есть, цитируя документацию , этот уникальный! "удаляет дубликаты элементов из себя. Возвращает ноль, если не было внесено никаких изменений (то есть дубликаты не найдены)."

Кто-нибудь может объяснить это, что мне кажется совершенно нелогичным? Это означает, что вместо возможности написать одну строчку кода ниже, добавив .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 к первой строке. Однако позже в той же программе я помещаю элементы в другой массив внутри цикла. Затем, в цикле, я хотел бы "де-дублировать" массив, но я не осмелился написать 'hooks_tested.uniq!' потому что он может вернуть ноль; вместо этого я должен написать hooks_tested = hooks_tested.uniq

Действительно, я утверждаю, что это особенно вопиющая ошибка в том, что это хорошо известный принцип, что при разработке метода, возвращающего массив, всегда следует по крайней мере возвращать пустой массив, а не ноль

Ответы [ 5 ]

10 голосов
/ 20 января 2010

Это потому, что 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
5 голосов
/ 20 января 2010

Восклицательный знак на 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
2 голосов
/ 20 января 2010

Вы можете добавить uniq (без восклицательного знака в конце) в конец первой строки.

Или, если вы настаиваете на использовании uniq!, используйте

(hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)).uniq!
1 голос
/ 08 сентября 2016

Начиная с 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
1 голос
/ 08 сентября 2016

Это не ответ на вопрос, а скорее обходной путь.

Поскольку uniq не возвращает nil, я использую uniq и присваиваю результат новой переменной вместо использования версии взрыва

original = [1,2,3,4]
new = original.uniq

#=> new is [1,2,3,4]
#=> ... rather than nil

Наличие новой переменной - небольшая цена. Он, безусловно, превосходит выполнение проверок if с повторными сложными вызовами uniq! и uniq и проверкой nil

...