Поведение методов Array bang

Некоторая ошибка в методах Array похожа на compact!, reject!, flatten!, uniq! return nil, если никаких изменений не было сделано:

[1,[2]].flatten!
# => [1, 2]
[1,2].flatten!
# => nil
[1,[2]].flatten
# => [1, 2]
[1,2].flatten
# => [1, 2]

[1,2,nil].compact!
# => [1, 2]
[1,2].compact!
# => nil
[1,2,nil].compact
# => [1, 2]
[1,2].compact
# => [1, 2]

Если они сделали это таким образом, должна быть причина. Любые идеи, что это может быть?

Ответы

Ответ 1

Методы bang (!) модифицируют текущий объект на месте, но они возвращают nil, если на нет соответствующих элементов. Это полезно, если по какой-либо причине вам нужно что-то сделать, если вы модифицировали данный массив.

if array.flatten!
  puts "Oh yeah... flattened that array!"
end

Ответ 2

У меня всегда было впечатление, что bang версия методов Arrayтолько разные в том смысле, что они изменить объект на месте.

Возможно, проблема заключается в том, что это впечатление не совсем правильное: согласно Дэвиду А. Черному, ! не означает, что метод изменяет приемник; ! означает, что этот метод является "опасной" версией другого эквивалентного метода, который имеет то же имя минус !.

Теперь опасность принимает множество форм (внимание мое):

Иногда вы получаете более одного вида "опасности" даже в течение одного взрыва метод. Возьмите String#gsub!. Эта метод изменяет приемник:

str = "David" 
str.gsub!(/$/, " Black")
str                        # David Black

Он также отличается от gsub (non-bang) в том случае, если строка не изменяется, gsub возвращает копию неизменной string, но gsub! возвращает nil:

str.gsub(/xyz/, "blah")    # David Black
str.gsub!(/xyz/, "blah")   # nil
str                        # David Black

The! в gsub! дает вам хедз-ап: он предупреждает вас об опасности, и это означает что перед использованием метода вы должен точно выяснить, как это ведет себя. (Простой "ri String#gsub!" должен это сделать.)

Эта семантика "хэдз-ап" также применяется к методам bang Array.