В следующем коде, почему переменная 'b' изменяется после вызова 'addup'?
Переменная не изменяется. Он по-прежнему ссылается на тот же самый массив, что и раньше.
Есть только два способа изменить переменную в Ruby:
- Назначение (
foo = :bar
)- Отражение (
Binding#local_variable_set
,Object#instance_variable_set
,Module#class_variable_set
,Module#const_set
)Ни один из них не используется здесь.
Кажется, я понимаю, почему «a» изменяется (хотя его немного нечеткое)
a
также не изменяется. Он также по-прежнему ссылается на тот же массив, что и раньше. (Который, кстати, тот же массив, который ссылается наb
.)Единственное, что делает , - это внутреннее состояние массива, на которое ссылаются оба
a
] иb
. Итак, если вы действительно понимаете, почему изменяется массив, на который ссылаетсяa
, вы также понимаете, почему массив, на который ссылаетсяb
, изменяется , поскольку он является тем же самым массивом , В вашем коде есть только один массив.Проблема с немедленным с вашим кодом заключается в том, что если вы хотите получить копию массива, то вам действительно нужно сделать копию массив. Вот что
Object#dup
иObject#clone
для:b = a.clone
Исправит ваш код.
НО!
В коде есть некоторые другие проблемы. Основная проблема - мутация . Если это вообще возможно, вы должны избегать мутации (и побочных эффектов в целом, из которых мутация является только одним примером), насколько это возможно, и использовать ее только тогда, когда вы действительно, ДЕЙСТВИТЕЛЬНО должны. В частности, вы должны never мутировать объекты, которые у вас нет, и это означает, что вы должны never мутировать объекты, которые были переданы вам в качестве аргументов.
Однако в вашем методе
addup
вы мутируете массив, который передается вам какarr
. Мутация является источником вашей проблемы, если вы не мутировалиarr
, а вместо этого вернули новый массив с необходимыми изменениями, тогда у вас не было бы проблемы в первую очередь. Один из способов не мутировать аргумент - это переместитьclone
в метод, но есть еще лучший способ.Еще одна проблема с вашим кодом заключается в том, что вы используете цикл. В Ruby почти никогда не бывает ситуации, когда петля является лучшим решением. Фактически, я бы зашел так далеко, чтобы утверждать, что если вы используете цикл, вы делаете это неправильно.
Петли подвержены ошибкам, трудно понять, трудно получить право, и они зависят от от побочных эффектов. Цикл не может работать без побочных эффектов, но мы просто сказали, что хотим избежать побочных эффектов!
Пример: ваш цикл содержит серьезную ошибку. Если я пройду
[1, 2, 3, 4, 5]
, результат будет[1, 2, 3, 5]
. Зачем? Из-за мутации и ручного цикла:На четвертой итерации цикла в начале массив выглядит следующим образом:
[1, 2, 3, 4, 5] # ↑ # i
После вызова
delete_at(i)
массив выглядит следующим образом:[1, 2, 3, 5] # ↑ # i
Теперь вы увеличиваете
i
, поэтому ситуация выглядит так:[1, 2, 3, 5] # ↑ # i
i
теперь больше длины из массива, эрго, концы цикла и5
никогда не удаляются.Что вы действительно хотите, это:
def addup(arr) arr.reject {|el| el > 3 } end a = [1, 2, 3, 4, 5] b = a puts "a=#{a}" # => [1, 2, 3, 4, 5] puts "b=#{b}" # => [1, 2, 3, 4, 5] puts "addup=#{addup(a)}" # => [1, 2, 3] puts "a=#{a}" # => [1, 2, 3, 4, 5] puts "b=#{b}" # => [1, 2, 3, 4, 5]
Как вы можете видеть, ничего не было мутировано.
addup
просто возвращает новый массив с теми изменениями, которые вы хотите. Если вы захотите позже обратиться к этому массиву, вы можете назначить его переменной:c = addup(a)
Нет необходимости вручную запускать скриптовые индексы. Нет необходимости копировать или клонировать что-либо. На расстоянии нет «жуткий действия», как это называл Альберт Эйнштейн. Мы исправили две ошибки и удалили 7 строк кода, просто
- , избегая мутации
- , избегая циклов