Какова лучшая практика в Ruby, чтобы избежать неправильного использования присваивания "="? - PullRequest
3 голосов
/ 26 марта 2011

Я был укушен пару раз, забыв, что x = y в Ruby заставляет x ссылаться на тот же объект, что и y;Я слишком привык к языкам, где это означает, в терминах Ruby, x = y.dup.Забыв об этом, я непреднамеренно меняю y, когда считаю, что в правой части задания безопасно.

Я вижу, что имеет смысл избегать простых x = y назначений без особой причины, но то же самое может скрываться в других местах, таких как

name = (person.last_name.blank? ? 'unknown' : person.last_name)

, где позжеname << title на самом деле будет менять person.last_name, а не просто имя.

Если это случилось и с вами, как вы научились избегать этого?Есть ли определенные красные флаги или шаблоны для поиска?Вы с подозрением смотрите на каждое задание, которое вы делаете?Вы часто используете .dup?Я не знаю, станет ли использование Ruby второй натурой для меня, поэтому любые полезные советы будут приветствоваться.

Ответы [ 4 ]

5 голосов
/ 26 марта 2011

Это может звучать неортодоксально в (по сути, обязательном) языке, таком как Ruby, но мой совет: избегайте побочных убытков , вообще не обновляя объекты (кроме случаев, когда это строго необходимо); вместо этого создайте новые. Вы платите немного за производительность, но получаете код, который является более четким, более компактным, более модульным и более простым для отладки.

http://en.wikipedia.org/wiki/Functional_programming

Итак, в вашем примере просто создайте новую строку с новым именем:

complete_name = name + title
1 голос
/ 26 марта 2011

Просто дополнение к ответу Tokland:

Функциональный подход настаивает на неизменности - т.е. не изменяет существующие объекты, но создает другой, когда вы хотите изменить исходный.Это несколько противоречит объектно-ориентированной парадигме, которую приносит также Ruby (объекты сохраняют свое внутреннее состояние, которое можно изменить, вызывая для него методы), поэтому вам нужно немного балансировать между двумя подходами (с другой стороны, нам выгодноимея несколько парадигм, легко доступных на одном языке).

Итак, запомните три вещи:

  1. Узнайте, что такое назначение в Ruby: ничего, кроме именования объекта.Итак, когда вы говорите y=x, вы говорите только: «мы даем другое имя y тому, что было названо x».
  2. name << title мутирует объект с именем name.
  3. name += title принимает объекты с именами name и title, объединяет их в другой объект и назначает этому новому имени объекта name.Ничего не мутирует.
0 голосов
/ 26 марта 2011

Метод не должен изменять переменную (например, с помощью оператора shift), если в его определении не указано, что он изменит его.

Итак: никогда не изменяйте объект в методе, который тоже не (а)создайте его или (b) документ, который это изменяет.

0 голосов
/ 26 марта 2011

Я тоже сталкивался с такой ситуацией, и это привело к ошибке, на которую я потратил полдня, чтобы разобраться.По сути, я сделал что-то вроде этого

hash = {....}
filename = object.file_name
hash.each |k, v| {file_name.gsub!(k, v) if file_name.include? k}

Этот код был внутри цикла, и в цикле я ожидал, что переменная file_name будет снова установлена ​​в исходное значение.Но имя объекта.file_name было изменено, так как я выполнял file_name.gsub!.Есть 2 способа решить эту проблему.Либо замените вызов .gsub! на file_name = file_name.gsub, либо наберите file_name = object.file_name.dup.Я выбрал второй вариант.

Я думаю, что мы должны быть осторожны с методами, имеющими ! и <<, так как они изменяют исходный объект, с которым они действуют, особенно после таких назначений.

...