pass by reference in each loop

X

Xiong Chiamiov

I have a list of variables that I need to pass through a modifying
function. Rather than specify for each one, I have them in an array,
and I loop through the array:

wml_needed = [@question, @answer_explanation]
wml_needed.each {|chunk| chunk = wml(chunk)}

The problem is that, unlike practically everything else in Ruby, each
loops don't pass by reference, so my assignments get lost.

Just to make sure, calling the function directly *does* work as
expected:

@question = wml(@question)

In PHP, I can add an ampersand (&) to the name (chunk, in this example)
to pass by reference instead of by copy; is there something similar in
Ruby, or should I be approaching this with a different method?

I realize that there are only two items in this example, but I'm
building for having many more.
 
T

Tim Hunter

Xiong said:
I have a list of variables that I need to pass through a modifying
function. Rather than specify for each one, I have them in an array,
and I loop through the array:

wml_needed = [@question, @answer_explanation]
wml_needed.each {|chunk| chunk = wml(chunk)}

The problem is that, unlike practically everything else in Ruby, each
loops don't pass by reference, so my assignments get lost.

Just to make sure, calling the function directly *does* work as
expected:

@question = wml(@question)

In PHP, I can add an ampersand (&) to the name (chunk, in this example)
to pass by reference instead of by copy; is there something similar in
Ruby, or should I be approaching this with a different method?

I realize that there are only two items in this example, but I'm
building for having many more.

Check out Array#map!
 
A

Anton Ivanov

Xiong said:
The problem is that, unlike practically everything else in Ruby, each
loops don't pass by reference, so my assignments get lost.

You're confused. The variable chunk contains a reference to an object.
You are assigning a different reference to that variable, that's all.

For your problem, look into map (or map!)
 
X

Xiong Chiamiov

Anton said:
You're confused. The variable chunk contains a reference to an object.
You are assigning a different reference to that variable, that's all.

Ah, that makes sense. I was a bit puzzled by that behavior, since it
seemed like everything in Ruby passed by reference, but your explanation
helped me get it straight in my mind.

Tim said:
Check out Array#map!

That looks exactly right. Thanks!
 
R

Robert Klemme

Ah, that makes sense. I was a bit puzzled by that behavior, since it
seemed like everything in Ruby passed by reference, but your explanation
helped me get it straight in my mind.



That looks exactly right. Thanks!

Note that you can do

@question, @answer_explanation =
[@question, @answer_explanation].map {|x| f(x)}

Btw, if this happens often, you probably rather want your variables in
an array anyway.

Cheers

robert
 
P

Pedro Silva

Hi,
The problem is that, unlike practically everything else in Ruby, each
loops don't pass by reference, so my assignments get lost.

(...)

In PHP, I can add an ampersand (&) to the name (chunk, in this example)
to pass by reference instead of by copy; is there something similar in
Ruby, or should I be approaching this with a different method?

Ruby doesn't pass arguments by reference it passes them by value. What
is passed to functions/blocks is the reference value. When you're
assigning something to that variable, inside the block, you're getting a
new object assigned to that variable and that's why it won't work. When
you change an object that a variable holds reference to, you're changing
that object, each time you assign you're placing a reference to a new
object into that variable.

Creating an array let you pass the reference of that array, using that
reference value you can change it's content. The method map! changes the
object (! is a convention used in Ruby that indicates the method alters
the receiver) so it will work also. The last option, suggested by
Robert, assigns the result to the variable after method/block invocation
so it'll work also.

If you still have doubts about pass-by-reference vs pass-by-value see
how things work in C/C++ and in Java, the first is
pass-by-reference/pass-by-copy and the other pass-by-value.

Pedro Silva.
 
X

Xiong Chiamiov

Pedro said:
Ruby doesn't pass arguments by reference it passes them by value. What
is passed to functions/blocks is the reference value.
Although that sounds exactly like passing by reference (doesn't it?),
I've come to understand today the nuance of how Ruby acts differently
(at least, I think).

In, say, C (ugh!), if I have some variable x and pass it to a function
by reference, it modifies the original x. If I pass by copy instead, it
copies it and modifies the copy, but not the original.
Ruby, however, (correct me if I'm wrong!) passes (essentially) by
reference. However, the difference is in how it treats the value
passed. When you modify it, instead of modifying the object that the
reference points to, you modify the reference itself to point to
something else.

This brings me back to my original question. While map! looked exactly
right, it modifies where the references in the array point to, rather
than modifying the values they pointed to, which means that my original
variables are not modified.

Robert said:
@question, @answer_explanation =
[@question, @answer_explanation].map {|x| f(x)}
which *does* work, as it assigns the values back to the variables, but
it defeats the purpose of the thing.

Perhaps I should explain a little bit better why I'm going about this:

I'm building a lightweight parsing language. The function wml() parses
this. I have a lot of user input, some of which I want to parse, but
some I don't.

So then, I wanted to avoid writing:
@a = wml(@a)
@b = wml(@b)
@c = wml(@c)
...
because the list could potentially be quite long, and I'm the good kind
of lazy (the one referred to in the Camel book).

Robert's solution would allow me to only write the function name once,
but the variables I'd still have to type twice, and I really like DRY.

My first thought was to store a list of all those variables in an array
and loop through them, as it's then very easy to add an additional
variable. Perhaps I'm approaching this the complete wrong way, though?
 
R

Robert Klemme

Robert's solution would allow me to only write the function name once,
but the variables I'd still have to type twice, and I really like DRY.

My first thought was to store a list of all those variables in an array
and loop through them, as it's then very easy to add an additional
variable. Perhaps I'm approaching this the complete wrong way, though?

That's quite near to what I suggested: just drop all the @a, @b etc. and
stuff everything in an array - or have multiple arrays, one per class of
data (i.e. data which undergoes wml and data which does not). Anyway,
chances are that you'll be able to find an elegant solution without
needing to change the language.

Kind regards

robert
 
X

Xiong Chiamiov

Robert said:
That's quite near to what I suggested: just drop all the @a, @b etc. and
stuff everything in an array - or have multiple arrays, one per class of
data (i.e. data which undergoes wml and data which does not). Anyway,
chances are that you'll be able to find an elegant solution without
needing to change the language.

Kind regards

robert

I don't think I understood what you were suggesting the first time. If
I understand what you're suggesting, it is to have the values themselves
in the array, rather than variables pointing *to* those values, yes?

I need to know specifically which values are which, however, as they get
passed back into different parts of the output, so I'm thinking I'd have
to use a hash, something like

hash = {
'a' => 'this is actually input',
'b' => 'not hardcoded',
...
}

so that I could assign them appropriately afterwards:

hash.each {|key, value| key = value}

although, looking at that now, I don't think that it would work.

But as far as hash mapping (if that's what I have to do), I suppose I
could use one of the methods in this thread
(http://www.nabble.com/Iterating-through-a-hash-td19074540.html), right?

I appreciate your help.
 
A

Anton Ivanov

Xiong said:
I don't think I understood what you were suggesting the first time. If
I understand what you're suggesting, it is to have the values themselves
in the array, rather than variables pointing *to* those values, yes?

I need to know specifically which values are which, however, as they get
passed back into different parts of the output, so I'm thinking I'd have
to use a hash, something like

hash = {
'a' => 'this is actually input',
'b' => 'not hardcoded',
...
}

so that I could assign them appropriately afterwards:

hash.each {|key, value| key = value}

although, looking at that now, I don't think that it would work.

But as far as hash mapping (if that's what I have to do), I suppose I
could use one of the methods in this thread
(http://www.nabble.com/Iterating-through-a-hash-td19074540.html), right?

I appreciate your help.

I think you're getting yourself more and more confused. Quit posting,
fire up irb and play around. What do you get when you do hash.each
{|key, value| key = value} ? Is there any difference between
[1,2,3].each {|x|} and a,b,c=1,2,3; [a,b,c].each {|x|}? etc.
 
X

Xiong Chiamiov

Anton said:
I think you're getting yourself more and more confused. Quit posting,
fire up irb and play around. What do you get when you do hash.each
{|key, value| key = value} ? Is there any difference between
[1,2,3].each {|x|} and a,b,c=1,2,3; [a,b,c].each {|x|}? etc.

I think that the solution is the only part that confuses me now. I've
done quite a bit of playing around with irb, which is how I got to
understanding that I can't seem to do what I want (DRY) with the method
I was trying to use (variables in an array). What I'm trying to find
now is if there *is* a solution that will work for my particular
circumstances.
 
R

Robert Klemme

2008/9/12 Xiong Chiamiov said:
I don't think I understood what you were suggesting the first time. If
I understand what you're suggesting, it is to have the values themselves
in the array, rather than variables pointing *to* those values, yes?

What exactly do you mean by "variable pointing to those values"? This
wording does not really seem to make sense in Ruby. The value
(object) does not care how many references are around pointing to it.
With

@x = Object.new
@y = Object.new
@a = [@x, @y]

and

@a = [Object.new, Object.new]

there is no difference with regard to array contents. I suggest to
use the latter and drop the former *if* you have to treat them
uniformly more often than not.
I need to know specifically which values are which, however, as they get
passed back into different parts of the output, so I'm thinking I'd have
to use a hash, something like

hash = {
'a' => 'this is actually input',
'b' => 'not hardcoded',
...
}

so that I could assign them appropriately afterwards:

hash.each {|key, value| key = value}

although, looking at that now, I don't think that it would work.

It won't.
But as far as hash mapping (if that's what I have to do), I suppose I
could use one of the methods in this thread
(http://www.nabble.com/Iterating-through-a-hash-td19074540.html), right?

Yep. Problem is, you present just a very tiny piece of your
application so it is extremely hard for use to suggest an appropriate
solution.

Cheers

robert
 
X

Xiong Chiamiov

Robert said:
What exactly do you mean by "variable pointing to those values"?

Well, I'm not quite sure how to explain it. I think you cleared up what
I was thinking right after this part, though:

Robert said:
The value
(object) does not care how many references are around pointing to it.
With

@x = Object.new
@y = Object.new
@a = [@x, @y]

and

@a = [Object.new, Object.new]

there is no difference with regard to array contents.
Yep. Problem is, you present just a very tiny piece of your
application so it is extremely hard for use to suggest an appropriate
solution.

Ah, I knew I had part of it online somewhere:
http://production.xyztextbooks.com/wml.php/generator . This is the PHP
version (before I converted it to Rails), so it hasn't had the benefit
of some bugfixes and such, but you can at least *see* what I'm talking
about.

This page (http://production.xyztextbooks.com/wml.php/generator/syntax)
has some of the basic syntactical things for this. Essentially, there
is an application called Webwork that uses a special dialect of Perl
intermingled with Tex, and I'm created a front-end to problem-creation,
which normally involves writing code that most teachers are not
comfortable with (see http://webwork.maa.org/wiki/SampleProblem1).

So then, it's just a simple form (using form_tag, I believe), and I grab
all of the request parameters at the top of the script. The question,
answer and answer_explanation all need to be run through a method that
parses the wml (as I'm calling it) into Perl/Tex code appropriate for
the problem, for instance, from ==3/5== to \(frac{3}{5}\).

As you can see, it's currently only those three fields that I need to
parse; I don't want to use extra resources running everything through
parsing. In the future, though, I see a larger number of fields that
need to be parsed, which is what I'm trying to write for.

Currently none of this is in any sort of model (since it's not going in
a database, I thought it shouldn't be), but I'm thinking that perhaps
that would be a better way of doing things, and may simplify this whole
thing. Unfortunately, I'm not familiar enough with models to know if
it's something I should spend my time figuring out; if it is, I'm glad
to struggle through getting it to work, but I have other things to fix
if that won't be worthwhile.

So, the short of it is, that site is what I'm doing, for the most part.

And apologies if I seem a little on edge; while I've found the ruby
community to be quite helpful, I miss my PHP manual, with all of its
user comments that are ever-so-helpful. The more popular a language,
the easier it is to find things on it, so a popular language can be
easier to learn than a less popular one, even if the less popular
actually has a more gentle learning curve.
 
R

Robert Klemme

2008/9/12 Xiong Chiamiov said:
Robert said:
What exactly do you mean by "variable pointing to those values"?

Well, I'm not quite sure how to explain it. I think you cleared up what
I was thinking right after this part, though:

Robert said:
The value
(object) does not care how many references are around pointing to it.
With

@x = Object.new
@y = Object.new
@a = [@x, @y]

and

@a = [Object.new, Object.new]

there is no difference with regard to array contents.
Yep. Problem is, you present just a very tiny piece of your
application so it is extremely hard for use to suggest an appropriate
solution.

Ah, I knew I had part of it online somewhere:
http://production.xyztextbooks.com/wml.php/generator . This is the PHP
version (before I converted it to Rails), so it hasn't had the benefit
of some bugfixes and such, but you can at least *see* what I'm talking
about.

This page (http://production.xyztextbooks.com/wml.php/generator/syntax)
has some of the basic syntactical things for this. Essentially, there
is an application called Webwork that uses a special dialect of Perl
intermingled with Tex, and I'm created a front-end to problem-creation,
which normally involves writing code that most teachers are not
comfortable with (see http://webwork.maa.org/wiki/SampleProblem1).

So then, it's just a simple form (using form_tag, I believe), and I grab
all of the request parameters at the top of the script. The question,
answer and answer_explanation all need to be run through a method that
parses the wml (as I'm calling it) into Perl/Tex code appropriate for
the problem, for instance, from ==3/5== to \(frac{3}{5}\).

As you can see, it's currently only those three fields that I need to
parse; I don't want to use extra resources running everything through
parsing. In the future, though, I see a larger number of fields that
need to be parsed, which is what I'm trying to write for.

So, one way to do it would be this:

class WhateverYouNameIt
NEEDS_PARSING = [
:foo,
:bar,
]

def initialize
@inputs = {}
end

# code that fetches input from somewhere

def parse
NEEDS_PARSING.each do |label|
@inputs[label] = wml(@inputs[label])
end
end
end

But then again, whether this approach is better ultimately depends on
- as I said - whether you more often than not need to treat those
inputs uniformly. This I cannot know and in case you are not, then
I'd just plain write the statements in code with all variables, i.e.

@foo = wml(@foo)
@bar = wml(@bar)

even if it looks tedious. Verbosity can be better at times because it
makes things clearer.

Cheers

robert
 
X

Xiong Chiamiov

fr what i can guess and gather, i think you need a hash with vars as
keys whose values you want to change anytime

As I thought more and more about this, I began to realize that I already
had such a thing created for me, since all of these variables I was
pulling directly out of request parameters.

So, before, I had something along these lines:
http://pastebin.com/f55d4b664

And now, I modify the values directly in the params hash, then pass them
along to the template at the end: http://pastebin.com/fa6863e

better to give us a sample php code

Once again, I was being silly and forgot that this project resides on
GitHub: http://github.com/xiongchiamiov/webwork/

Rick said:
This may or may not help you understand the relationship between
variables,
references, and objects in Ruby. I wrote it quite some time ago.

http://talklikeaduck.denhaven2.com/articles/2006/09/13/on-variables-values-and-objects

That article firmed up for me that Ruby treats variables the same way as
Python, at least in my limited experience with both.

The part that it made me realize, though was that the way Ruby is
structured allows you to put values in an array, then modify the
originals, and the array is changed:

irb(main):001:0> a = 1
=> 1
irb(main):002:0> b = 2
=> 2
irb(main):003:0> c = 3
=> 3
irb(main):004:0> arr = [a, b, c]
=> [1, 2, 3]
irb(main):005:0> a = 0
=> 0
irb(main):006:0> arr
=> [0, 2, 3]

But not what I was trying to do, which is to change the array, and have
the values propagate backwards:

irb(main):001:0> a = 1
=> 1
irb(main):002:0> b = 2
=> 2
irb(main):003:0> c = 3
=> 3
irb(main):004:0> arr = [a, b, c]
=> [1, 2, 3]
irb(main):005:0> arr[0] = 0
=> 0
irb(main):006:0> a
=> 1
 
D

David Masover

But not what I was trying to do, which is to change the array, and have
the values propagate backwards:

irb(main):001:0> a = 1
=> 1
irb(main):002:0> b = 2
=> 2
irb(main):003:0> c = 3
=> 3
irb(main):004:0> arr = [a, b, c]
=> [1, 2, 3]
irb(main):005:0> arr[0] = 0
=> 0
irb(main):006:0> a
=> 1

I'm not sure if you were asking for help, but I wanted to show off.
Ruby doesn't work the way you describe, but it can fake it pretty well.

You're going to have to use something other than variables, like methods:

arr = [1,2,3]
def a
arr[0]
end
def b
arr[1]
end
def c
arr[2]
end

That's a lot of repetition, though, so I'd do this instead:

arr = [1,2,3]
%w{a b c}.each_with_index do |name, index|
define_method name do
arr[index]
end
end

Or, if you're only going to be manipulating it once, you could just unpack the
array into variables when you're done:

arr = [1,2,3]
arr[0] = 0
a,b,c = arr

Or you could bypass the whole issue and use a hash instead of an array, if
that fits what you're looking for.

One more solution: Make something that looks like an array, but isn't:

class FakeArray
VARS = [:a, :b, :c]
attr_accessor *VARS

def [] index
self.send VARS[index]
end
def []= index, value
self.send "#{VARS[index]}=", value
end

def what_you_wanted
a = 1
b = 2
c = 3
self[0] = 0
puts "a is #{a}"
end

# And if you want it to be even more array-like:
include Enumerable
def each
VARS.each do |var|
yield(self.send var)
end
end

def length
VARS.length
end
end


I'll leave it as an exercise to the reader to figure out how to separate the
pseudo-array from the class where a, b, and c are defined. This provides a
bit more flexibility, as you could then have more than one pseudo-array:

arr1 = self.pseudo_array:)a, :b, :c)
arr2 = self.pseudo_array:)c, :b, :a)

arr1[0] = 42 # or whatever

# This should be true, then:
arr1[0] == arr2[2] == self.a



There is one possibility I haven't covered, which is how to actually wrap a
variable in a reference. I haven't covered that mostly because it isn't
really helpful here -- no matter how magical I make my IntegerWrapper, it
will be completely discarded when you do:

arr[0] = 0

Because that "arr[0] =" is calling a method on the array itself, not doing
anything to the object stored inside it.
 
B

Brian Candler

I don't think anybody has mentioned String#replace yet. Given the
original code:
wml_needed = [@question, @answer_explanation]
wml_needed.each {|chunk| chunk = wml(chunk)}

and assuming that @question and @answer_explanation refer to String
objects, then it can be made to work as follows:

wml_needed.each {|chunk| chunk.replace(wml(chunk)) }

Quick explanation:

* @question contains a reference (pointer) to a string, let's call it
str1
* @answer_explanation contains a reference to another string, str2
* wml_needed is a reference to an newly-created array which also
contains references to str1 and str2
* using <ref>.replace, str1 and str2 are modified *in place*. All the
existing references still point to the original strings, but those
strings have mutated.
* since @q/@a and wml_needed both point to the same places, str1 and
str2, both "see" the updated versions of str1 and str2

However I find this is rarely needed - and note that it doesn't work for
objects which cannot be mutated (e.g. numbers).

If you need to modify instance variables, and there are only two, then
using the KISS principle you can just write

@question = wml(@question)
@answer = wml(@answer)

Here, wml() presumably returns a new string in each case, and so
@question and @answer are updated to point to the new string; the old
ones will be garbage-collected at some point in the future, as long as
there are no other live references to them.

If that's what you're actually trying to do, but you want to apply the
DRY principle, then it *is* possible to read and write instance
variables in a loop:

[:mad:question, :mad:answer].each do |iv|
instance_variable_set(iv, wml(instance_variable_get(iv)))
end

Or, if you class has accessor methods (question, question=, answer and
answer=) you can do

["question", "answer"].each do |iv|
send("#{iv}=", wml(send(iv)))
end

The latter example in particular is worth working through until you
understand how it works.

Of course, if your data naturally sits in an array, then you could just
keep a single instance variable which refers to the array, and don't
keep duplicate instance variables. Then you can just use map or map! to
update the array.

HTH,

Brian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,252
Latest member
MeredithPl

Latest Threads

Top