Why does variable not change value in an '.each' where an assignment is happening?

I have this line, where the input variables in the array can come in any
form — string, integer, fixnum, float (ps, I know that sales_price as
a
currency should really be BigDecimal or the like on instantiation, this
is
just to illustrate my question):

[quantity, sales_price].each { |multiplicand| multiplicand =
BigDecimal.new(multiplicand.to_s) if multiplicand.class != BigDecimal}

So if
(rdb:1) quantity = 50
(rdb:1) quantity.class
Fixnum
(rdb:1) sales_price = 50
(rdb:1) sales_price.class
Float

And I call this:
(rdb:1) [quantity, sales_price].each { |multiplicand| multiplicand =
BigDecimal.new(multiplicand.to_s) if multiplicand.class != BigDecimal}
[50, 1.99]

Why don’t the variables change?
(rdb:1) quantity.class
Fixnum
(rdb:1) sales_price.class
Float
(rdb:1)

Because if I do this:
[quantity, sales_price].map { |multiplicand| multiplicand =
BigDecimal.new(multiplicand.to_s) if multiplicand.class != BigDecimal}

I do get:
[#BigDecimal:3b4f4f8,‘0.5E2’,4(8),
#BigDecimal:3b4f46c,‘0.199E1’,8(8)]

… so I know that the logic is working.

Shouldn’t an operation in an each change the input? — I thought the
input
should be by ref.

On Feb 8, 2:44pm, David K. [email protected] wrote:

I have this line, where the input variables in the array can come in any
form — string, integer, fixnum, float (ps, I know that sales_price as a
currency should really be BigDecimal or the like on instantiation, this is
just to illustrate my question):

[quantity, sales_price].each { |multiplicand| multiplicand =
BigDecimal.new(multiplicand.to_s) if multiplicand.class != BigDecimal}

[snip]

All each does is iterate over a collection, yielding to the block.
when you do multiplicand = blah, you are just setting the local
variable multiplicand to point at some new object - you’re not
mutating the actual object.

Fred

On Tue, Feb 8, 2011 at 9:06 AM, Frederick C.
<[email protected]

wrote:

[snip]

All each does is iterate over a collection, yielding to the block.
when you do multiplicand = blah, you are just setting the local
variable multiplicand to point at some new object - you’re not
mutating the actual object.

I see, I guess I got confused as I use ActiveRecord so much and within a
each block, by setting values and calling save it of course saves the
object. Is there a way to achieve what I am presenting, though, passing
a
batch of variables and having them operated on and changed?

This situation:
(rdb:1) v1 = 1
1
(rdb:1) v2 = 2
2
(rdb:1) v3 = 3
3
(rdb:1) arr = [v1,v2,v3]
[1, 2, 3]
(rdb:1) arr = arr.map{|i| i = i + 1}
[2, 3, 4]

However what I would want is to see the variable values changed but they
are
not:
(rdb:1) v1
1
(rdb:1) v2
2

Excuse my ingenuity, but I am trying to understand… if I have an array
of
variables it seems that when the array is created, the elements which
were
variables are substituted by values, and there is no reference:
(rdb:1) arr = [v1,v2,v3]
[1, 2, 3]
(rdb:1) v1
1
(rdb:1) arr[0]=5
5
(rdb:1) v1
1

This must sound like kindergarten but apparently I have been working
albeit
successfully under some very false premises.

On Tue, Feb 8, 2011 at 10:34 AM, Frederick C. <
[email protected]> wrote:

method on an object that changes its state, whereas
foo = bar just points a local variable at something new. (and in
addition Fixnums/BigDecimal are immutable - there is no method you can
call on a number to change it into another nmber)

I think I am starting to see, maybe. Hopefully not being too annoying
here.

It seems rather strange to me that if you have an object, i.e. a string
that
it should behave differently than my Account object… the string#=()
method is still a property, right? – no different really than
account#name=(), so seems it would be logical that if I can do
account.name= ‘x’ within an iterator, that doing string=‘y’ should
also work, but as we
see it does not (lets put the Fixnums and BigDecimal aside as I
understand
as a property they are re-created rather than modified).

arr.each {|account| account.name = ‘x’}

arr.each {|string| string=(‘y’)}

… they seem to me to be the same thing, unless objects are really not
the
same.

But with another experiment I see something interesting — if I
instantiate
a string formally as an object, then I do get the by reference
relationship
in an array:

ruby-1.9.2-p136 :098 > str = String.new(‘David’)
=> “David”
ruby-1.9.2-p136 :099 > arr = [str]
=> [“David”]
ruby-1.9.2-p136 :100 > arr.each {|s| str = ‘new val’}
=> [“David”]
ruby-1.9.2-p136 :101 > str
=> “new val”

But if I just do it as a normal assignment, I do not:

ruby-1.9.2-p136 :106 > string = ‘David’
=> “David”
ruby-1.9.2-p136 :107 > arr = [string]
=> [“David”]
ruby-1.9.2-p136 :108 > arr.each {|s| s = ‘new val’}
=> [“David”]
ruby-1.9.2-p136 :109 > string
=> “David”

I thought that doing

str = ‘David’

was just a shortcut to doing

str = String.new(‘David’)

But apparently the behavior at least in the context of an array is
different. Wow!

On Feb 8, 3:33pm, David K. [email protected] wrote:

On Tue, Feb 8, 2011 at 9:06 AM, Frederick C. <[email protected]

I see, I guess I got confused as I use ActiveRecord so much and within a
each block, by setting values and calling save it of course saves the
object. Is there a way to achieve what I am presenting, though, passing a
batch of variables and having them operated on and changed?

that is very different. doing some_object.foo = ‘bar’ is calling a
method on an object that changes its state, whereas
foo = bar just points a local variable at something new. (and in
addition Fixnums/BigDecimal are immutable - there is no method you can
call on a number to change it into another nmber)

Excuse my ingenuity, but I am trying to understand… if I have an array of
variables it seems that when the array is created, the elements which were
variables are substituted by values, and there is no reference:

(rdb:1) arr = [v1,v2,v3]
[1, 2, 3]

Without resorting to dirty tricks, you can’t change the values of
local variables other than by actually doing
v1 = 2. arr does NOT contain [v1,v2,v3]. It contains 3 objects, and
the local variables v1,v2,v3 just happen to also be referencing those
same objects.

Fred

Phil

On Tue, Feb 8, 2011 at 11:26 AM, David K.
[email protected]wrote:

each block, by setting values and calling save it of course saves the

relationship in an array:
But if I just do it as a normal assignment, I do not:

In the first case, you explicitly changed the value of the local
variable
str… it didn’t change the array contents. In the second case you only
changed the value inside the scope of the block that was enumerating
through. Whether you created the string with String.new or not won’t
make a
difference.

Ruby does pass object by reference, but when you do s = “whatever” in
the
block, you’re creating a new local variable that only lives as long as
the
scope of the block.

If you want to modify an array of strings, for example, you could do:

arr = [“John”, “Doe”]
=> [“John”,“Doe”]
arr.each{|s| s.replace(“blah”) }
=> [“blah”,“blah”]

s.replace actually modifies the object, instead of creating a new one.

On 8 February 2011 18:06, Phil C. [email protected] wrote:

If you want to modify an array of strings, for example, you could do:
arr = [“John”, “Doe”]
=> [“John”,“Doe”]
arr.each{|s| s.replace(“blah”) }
=> [“blah”,“blah”]
s.replace actually modifies the object, instead of creating a new one.

Also, depending on your use-case, consider the enumerable methods
“.inject”, “.collect”, et al:

x = [1,2,3]
=> [1, 2, 3]
x.collect { |y| y*y }
=> [1, 4, 9]

http://www.ruby-doc.org/core/classes/Enumerable.html

On Tue, Feb 8, 2011 at 2:17 PM, Michael P. [email protected]
wrote:

“.inject”, “.collect”, et al:

Thanks… #inject is new to me and looks very cool. From what I
understand
collect and map are the same function, is that your understanding too?

On Tue, Feb 8, 2011 at 12:06 PM, Phil C.
[email protected]wrote:

It seems rather strange to me that if you have an object, i.e. a string

ruby-1.9.2-p136 :099 > arr = [str]
ruby-1.9.2-p136 :107 > arr = [string]
through. Whether you created the string with String.new or not won’t make a
difference.

Oh, right, I see my errror

s.replace actually modifies the object, instead of creating a new one.

Perfect, now I think I’ve got it, thanks!

On 8 February 2011 20:35, David K. [email protected] wrote:

On Tue, Feb 8, 2011 at 2:17 PM, Michael P. [email protected] wrote:

Also, depending on your use-case, consider the enumerable methods
“.inject”, “.collect”, et al:

Thanks… #inject is new to me and looks very cool. From what I understand
collect and map are the same function, is that your understanding too?

That’s what the documentation says…