From: dblack@... Date: 2007-08-25T08:43:57+09:00 Subject: Re: A simple question regarding ruby method argument Hi -- On Sat, 25 Aug 2007, John Dearden wrote: > David A. Black wrote: > ...stuff deleted... >> Since integers are immutable (luckily!), and local variables are >> local, you can't change an integer object via a reference copied to a >> method. If it helps, I can assure you that you would *not* want this >> to happen. It would be very weird if my local variables bindings were >> at the mercy of methods that I called. >> >> What you're really looking for is an object container, and local >> variables don't really work that way. For container semantics you >> really need to use containers. You could do something like: > > First, thanks for the quick answer. That was unexpected. > > Unfortunately I can't say I understand the part of your response I > quoted above, other than that it says, "Nope, you can't do what you're > trying to do. Unless you're working with strings, which are somehow > different." > > The "local variables _bindings_" bit implies to me this has something to > do with Ruby's underlying storage that I have not yet grasped. I'm > still thinking of a 'local variable' as really some chunk of memory, and > the way the bits are set in that memory mean something in the context of > my program. If I pass that location to a subprogram, and know that it's > going to put something else in it, that's good. > > I can think of examples where I would like my subprogram to modify the > local variables of the calling code. For instance, I have two points, > and I want to calculate m and b for the formula of a line, as in y = m * > x + b. I would be inclined to write a subprogram calc_m_b(x1, y1, x2, > y2, var m, var b) (in Pascalese). I could do what amounts to the same > thing with two functions, one returning m and the other b, but that > seems sort of silly in this case. I could design an object that > represented a line in some fashion, and let it hold m and b, but that's > not always what you'd want, either. > > Maybe I'm not thinking in terms of objects enough yet? I agree my answer was sort of "You can't do the thing you just said you can't do" :-) But that is sort of how it is. It's axiomatic in Ruby that local variables are local. By "bindings" I mean: def x(a) a = 3 # a is bound to 3 end a = 1 # *this* (different) a is bound to 1 x(a) puts a # still 1 The call to x does not change the existing binding of a. The only kind of change you can do in a situation like this is when a variable is bound to a mutable object -- or, more accurately, a variable contains a reference to a mutable object (since basically Ruby variables traffic in references). That's where you get into things like: def add_to_array(a) a.push("New element") end array = [1,2,3] add_to_array(array) p array # [1,2,3,"New element"] If I use 'a' instead of 'array' for my array, the fact that add_to_array also uses 'a' is just coincidence. There's no connection between the two. It just happens that the identifier 'a' is used in two local scopes to handle references to the same object. The usual patterns you see in Ruby are either container-based, object-based (where you have an object with an attribute; you pass the object into the method and it does something to the attribute), or assignment-based but in the same scope: def change_me(a) a + 2 end a = 1 a = change_me(a) # a is now 3 I've pushed the assignment out of the method -- where it doesn't have any effect except to set or change the method's local variables -- into the calling context. That way, I can do whatever I want with my identifiers in each scope. David -- * Books: RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242) RUBY FOR RAILS (http://www.manning.com/black) * Ruby/Rails training & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)