Что лучше всего использовать в Ruby, чтобы избежать неправильного использования присваивания =?

Я пару раз был укушен, забыв, что 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 моей второй натурой, поэтому буду рад любым полезным советам.


person Mike Blyth    schedule 26.03.2011    source источник
comment
Просто любопытно, с какого языка вы переходите на Ruby?   -  person Mladen Jablanović    schedule 26.03.2011
comment
Mladen -- в основном короткие набеги на разные от сборки 6510 и IBM 360 до PL/I, Pascal, C++, Forth. Не эксперт ни в чем... обычно моя настоящая работа - быть врачом.   -  person Mike Blyth    schedule 26.03.2011
comment
На мой взгляд, это одна из тех вещей, на которые нужно обращать внимание при программировании. Паскаль использует := для присваивания и = для сравнения, интерпретируемый BASIC использует = для обоих, Perl использует eq для сравнения строк, == для числового сравнения и = для присваивания, и в основном вы должны держать их все прямо в голове. Вот почему так важно комментировать, писать чистый и понятный код; Достаточно сложно вернуться к коду, написанному месяцы или годы назад, а затем добавить другой язык с его уникальными особенностями... этого достаточно, чтобы заставить ваш мозг взорваться.   -  person the Tin Man    schedule 27.03.2011


Ответы (4)


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

http://en.wikipedia.org/wiki/Функциональное_программирование

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

complete_name = name + title
person tokland    schedule 26.03.2011
comment
Другими словами, используйте name += title вместо name << title выше. - person Mladen Jablanović; 26.03.2011
comment
@Младен. Не совсем так, если бы вы использовали функциональный подход, вы бы создали новую строку: name_with_title = name + title. Вопрос обновлен, чтобы прояснить его. - person tokland; 26.03.2011
comment
+1 За функциональное программирование. Хотелось бы, чтобы мои коллеги поняли этот принцип... - person fresskoma; 26.03.2011
comment
name += title создает новую строку (но присваивает ей то же имя name). - person Mladen Jablanović; 26.03.2011
comment
@Mladen: я знаю, но повторное использование одного и того же имени не рекомендуется. Как вы знаете, это одно из преимуществ FP, когда вы видите присваивание (привязка имени, в строгих функциональных языках), вы точно знаете, что оно будет иметь это же значение во всем своем объеме. - person tokland; 26.03.2011

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

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

Итак, три вещи, которые нужно запомнить на данный момент:

  1. Узнайте, что такое присваивание в Ruby: не что иное, как присвоение имени объекту. Итак, когда вы говорите y=x, вы говорите только «мы даем другое имя y тому, что было названо x».
  2. name << title мутирует объект с именем name.
  3. name += title берет объекты с именами name и title, объединяет их в другой объект и присваивает этому новому объекту имя name. Он ничего не мутирует.
person Mladen Jablanović    schedule 26.03.2011
comment
Я согласен, строгое функциональное программирование и классический ООП Ruby иногда трудно (или невозможно) совместить. Мое личное правило — стараться оставаться функциональным настолько, насколько это возможно; когда вам нужно изменить состояние объекта, пусть будет так. - person tokland; 28.03.2011

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

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

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

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

person rubyprince    schedule 26.03.2011
comment
функциональный подход был бы намного короче: hash.inject(object.file_name) { |string, (k, v)| строка.gsub(k, v) } - person tokland; 26.03.2011
comment
tokland - мне интересно узнать больше об использовании функционального подхода... Я только немного поигрался с Haskell. Где я могу узнать больше об использовании его с Ruby? - person Mike Blyth; 26.03.2011
comment
@tokland .. Я слышал о функциональном программировании, но у меня никогда не было возможности по-настоящему попробовать и понять его .. и да .. это было бы намного короче .. - person rubyprince; 27.03.2011
comment
@Майк, @rubyprince. Я написал статью об идиомах Ruby, в которой немного рассказал о функциональных конструкциях. Я планирую написать что-то конкретное о FP с Ruby, я опубликую URL-адрес здесь. На данный момент: code.google.com/p/tokland/wiki/RubyIdioms. - person tokland; 28.03.2011

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

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

person John Douthat    schedule 26.03.2011