2014-10-09 93 views
2

我是Ruby新手,并且无法理解此方法中发生了什么。Ruby - 方法更改输入变量值

我做在Rails控制器这一呼吁 -

@arr = SomeClass.find_max_option(params[:x], @pos, params[:y], some_var) 

我试图返回值@arr,成功地发生了,不过我的操作做出@pos该方法中被带回以及; @pos的值在我仅尝试获取@arr的值时发生了变化。

下面是关于该方法

#before going into the method 
@pos = [a,b] 

def self.find_max_option(x, pos, y, some_var) 

pos.collect! { |element| 
    (element == b) ? [c,d] : element 
    } 
    end 

#new value of pos = [a, [c,d]] which is fine for inside in this method 

... #some calculations not relevant to this question, but pos gets used to generate some_array 

return some_array 

但是,当结束该方法,并得到回控制器详情的@pos的值现在为[A,[C,D]]为好。

这是怎么回事?我认为pos将与@pos分开处理,并且价值不会带回。作为一种变通方法我刚才创建的方法中的一个新的局部变量,但我想知道这是发生

#my workaround is to not modify the pos variable 
pos_groomed = pos.collect { |element| 
    (element == b) ? [c,d] : element 
    } 
    end 
+0

您正在传递'@ pos'作为参数,这意味着'pos'是对'@ pos'的引用。你可以在'collect!'之前设置'pos = pos.dup'或者只使用非破坏性的'collect'。你的代码并不是直截了当,因为你正在返回一个不存在的变量 – engineersmnky 2014-10-09 18:18:01

+0

对于noob问题,我很抱歉,但这是所有Ruby方法的标准吗?对方法中传递的参数进行更改传播?所有参数默认都是输入/输出?我不记得这发生在Java中。 – 2014-10-09 18:20:53

+0

不要抱歉,这不是一个简单的问题,即使是这样,你也不应该感到抱歉。检查这个http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value它会帮助你与这个话题。 – Nobita 2014-10-09 18:26:27

回答

3

而不是使用collect!,只需用collect(不!)。所以,重写你的方法为:

def self.find_max_option(x, pos, y, some_var) 
    pos.collect { |element| 
    (element == b) ? [c,d] : element 
    } 
end 

当使用!版本的collect,要更换由块返回的值的每个元素。但是,在使用collect而不使用!时,会创建一个新数组,并且正在调用collect的对象不会被更改。请参阅该文档:

collect! VS collect

在方法名末尾使用!是在Ruby中一种常见的做法。 This question是相关的,值得一看。

+0

我仍然需要在方法中稍后使用'pos''(我没有显示整个东西,只是相关的部分),所以要使用pos.collect我需要声明一个新的变量像它一样我在我的解决方法,对不对?问题更多 - 为什么“@ pos”也被改变了。 – 2014-10-09 18:22:42

+0

作为这个答案的注释,红宝石约定是任何方法与!将会有“意想不到的副作用”。最经常的一个!会改变调用它的对象,但有时它会有其他副作用,如退出!退出时无需调用任何退出处理程序。当使用!方法,请始终查阅文档并确保您了解“其他”发生的情况。 – 2014-10-09 18:23:32

+0

您需要执行dup,因为@engineersmnky在评论中建议将该收集分配给方法中的另一个变量(就像您对pos_groomed所做的那样),然后使用该变量。 – Nobita 2014-10-09 18:24:16

1

您正在使用collect的破坏性版本。 破坏性方法更改调用方法的对象,而非破坏性方法返回新对象。

Ruby开发人员倾向于将这些方法称为“爆炸方法”,因为惯例是破坏性方法具有!后缀。

pos.collect! # changes pos and returns pos 
pos.collect # creates a new object 

您的解决方法只适用于您使用非破坏性收集,而原始代码使用收集!

pos.collect do |element| 
(element == b) ? [c,d] : element 
end 

应该工作得很好。

至于为何方法外的对象的变化:

在红宝石,当将参数传递到方法时,实际上是通过基准的对象。 因此,将数组传递给方法不会创建副本,而只是将引用传递给原始数组。 没有办法'按价值传递',但是如果你真的必须的话,你可以用dup或clone创建一个副本。

+0

我会小心地说'你正在传递对该对象的引用'。在Ruby中这不是真的。 Ruby严格按照价值传递。 – Nobita 2014-10-09 19:15:17

+0

是的,你是对的,它的价值传递,但价值是一个对象的引用。这有点棘手 – 2014-10-09 20:51:24

+0

我这样看:'变量是对象的引用'。 – Nobita 2014-10-09 20:58:15