Problem With eval and each_index

I am trying to set a group of variables to values stored in a tab delimited string. I have no problem splitting the string into an array, call it e, or in putting the variable names in a second array, call it v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123", "1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Am I missing something, or should I be trying a different method to accomplish this?

Michael W. Ryder wrote:

I am trying to set a group of variables to values stored in a tab delimited string. I have no problem splitting the string into an array, call it e, or in putting the variable names in a second array, call it v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123", "1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Am I missing something, or should I be trying a different method to accomplish this?

I just noticed that the above method does set name to "John Doe" which makes it even more confusing for me.

Inside a block, local variables are local to the block. So, you're
creating all those local variables and then the block ends and they are
all thrown away.

Why do you need variables called "name" (etc.) anyway? Why not use a
hash, so that h["name"] contains the name ("John Doe"), h["street"]
contains the street, and so on? So:

h = Hash.new
v.zip(e).each {|k,v| h[k]=v}

Notice that even here we define h before we get to the block. That way,
the block's h is our h; it is global to the block.

An even cooler approach is a struct:

Person = Struct.new(*(v.map {|i| i.to_sym}))
p = Person.new(*e)

Now you've got an object, p, where p.name is "John Doe" and so on.

m.

···

Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

I am trying to set a group of variables to values stored in a tab
delimited string. I have no problem splitting the string into an array,
call it e, or in putting the variable names in a second array, call it
v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123",
"1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

--
matt neuburg, phd = matt@tidbits.com, Matt Neuburg’s Home Page
Leopard - http://www.takecontrolbooks.com/leopard-customizing.html
AppleScript - http://www.amazon.com/gp/product/0596102119
Read TidBITS! It's free and smart. http://www.tidbits.com

or, better:

h = Hash[*v.zip(e).flatten]

···

On Fri, Mar 13, 2009 at 7:32 PM, matt neuburg <matt@tidbits.com> wrote:

Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

I am trying to set a group of variables to values stored in a tab
delimited string. I have no problem splitting the string into an array,
call it e, or in putting the variable names in a second array, call it
v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123",
"1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Inside a block, local variables are local to the block. So, you're
creating all those local variables and then the block ends and they are
all thrown away.

Why do you need variables called "name" (etc.) anyway? Why not use a
hash, so that h["name"] contains the name ("John Doe"), h["street"]
contains the street, and so on? So:

h = Hash.new
v.zip(e).each {|k,v| h[k]=v}

matt neuburg wrote:

I am trying to set a group of variables to values stored in a tab delimited string. I have no problem splitting the string into an array,
call it e, or in putting the variable names in a second array, call it
v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123", "1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Inside a block, local variables are local to the block. So, you're
creating all those local variables and then the block ends and they are
all thrown away.

I can see that if I create the variable before running the method the variable is set even after the method ends. Which of course brings up the question of why is the change visible outside of the block? This behavior is almost like a mix of C and Basic. You have to define a variable in C before you can use it and in Basic once a variable is set it is always visible.

Why do you need variables called "name" (etc.) anyway? Why not use a
hash, so that h["name"] contains the name ("John Doe"), h["street"]
contains the street, and so on? So:

I have been programming in Business Basic for over 20 years and am used to reading a file with a statement like:
   Read(1,Key="1234")Name$,Street$,City$,State$,Zip$,*,Telephone$
and then using those variables. I was trying to use what I am familiar with while learning Ruby. I realize in Ruby I would have to do read the string of data in, split it on the separator, and then assign it to the proper variables. Using a hash was an option but seemed to be more typing to use as I would have to enter: puts h["name"] vs puts name.

h = Hash.new
v.zip(e).each {|k,v| h[k]=v}

Notice that even here we define h before we get to the block. That way,
the block's h is our h; it is global to the block.

An even cooler approach is a struct:

Person = Struct.new(*(v.map {|i| i.to_sym}))
p = Person.new(*e)

Now you've got an object, p, where p.name is "John Doe" and so on.

I might try this for some things as it seems more logical than the hash for what I normally do. Thanks for the tip. I hadn't got into structures yet.

···

Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

m.

Right you are, sorry about that. m.

···

Christopher Dicely <cmdicely@gmail.com> wrote:

On Fri, Mar 13, 2009 at 7:32 PM, matt neuburg <matt@tidbits.com> wrote:
> Michael W. Ryder <_mwryder@worldnet.att.net> wrote:
>
>> I am trying to set a group of variables to values stored in a tab
>> delimited string. I have no problem splitting the string into an array,
>> call it e, or in putting the variable names in a second array, call it
>> v. The problem arises when I try to merge the two together. If I enter:
>> irb(main):073:0> p v
>> ["name", "street", "city", "state", "zip", "*", "telephone"]
>> => nil
>> irb(main):074:0> p e
>> ["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123",
>> "1234567890"]
>> => nil
>> irb(main):075:0> eval "#{v[0]} = e[0]"
>> => "John Doe"
>> irb(main):076:0> p name
>> "John Doe"
>> => nil
>>
>> it works as I want. But when I try:
>> irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
>> => ["name", "street", "city", "state", "zip", "*", "telephone"]
>>
>> and then enter: puts zip
>> it returns with a NameError saying that the variable was undefined.
>
> Inside a block, local variables are local to the block. So, you're
> creating all those local variables and then the block ends and they are
> all thrown away.
>
> Why do you need variables called "name" (etc.) anyway? Why not use a
> hash, so that h["name"] contains the name ("John Doe"), h["street"]
> contains the street, and so on? So:
>
> h = Hash.new
> v.zip(e).each {|k,v| h[k]=v}

or, better:

h = Hash[*v.zip(e).flatten]

--
matt neuburg, phd = matt@tidbits.com, Matt Neuburg’s Home Page
Leopard - http://www.takecontrolbooks.com/leopard-customizing.html
AppleScript - http://www.amazon.com/gp/product/0596102119
Read TidBITS! It's free and smart. http://www.tidbits.com

matt neuburg wrote:

I am trying to set a group of variables to values stored in a tab
delimited string. I have no problem splitting the string into an array,
call it e, or in putting the variable names in a second array, call it
v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123",
"1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Inside a block, local variables are local to the block. So, you're
creating all those local variables and then the block ends and they are
all thrown away.

I can see that if I create the variable before running the method the
variable is set even after the method ends. Which of course brings up the
question of why is the change visible outside of the block?

Ruby blocks are closures, and they therefore capture the environment
where they are created. So if a local variable exists in that
environment when the block is created, the block has access to it, and
changes will affect the externally-created local variable. But new
local variables introduced in the block are local to the block. In
essence, the environment of the block is subordinate to the
environment in which it is created.

Why do you need variables called "name" (etc.) anyway? Why not use a
hash, so that h["name"] contains the name ("John Doe"), h["street"]
contains the street, and so on? So:

I have been programming in Business Basic for over 20 years and am used to
reading a file with a statement like:
Read(1,Key="1234")Name$,Street$,City$,State$,Zip$,*,Telephone$
and then using those variables.

Assuming you have a "keyed_read" method defined properly, you can easily do:

name, street, city, state, zip, _, telephone =
data_file.keyed_read(:key=>"1234")

But the thing that is harder is dynamically assigning variable names
based on a array provided. Using hashes or structs are the more
idiomatic Ruby ways of doing something similar to that. Its possible
to do something very close to what you were attempting with instance
variables using Object#instance_variable_set.

irb(main):036:0> v.zip(e).each { |var, val|
irb(main):037:1* begin
irb(main):038:2* self.instance_variable_set(("@"+var).to_sym,val)
irb(main):039:2> rescue
irb(main):040:2> end
irb(main):041:1> }
=> [["name", "John Doe"], ["street", "123 Main St"], ["city",
"Anywhere"], ["state", "US"], ["zip", "01234-5678"], ["*", "ab123"],
["telephone", "1234567890"]]
irb(main):042:0> @name
=> "John Doe"

So if you want to do it that way, you can. (Note, I'm assuming that
the '*' is a special symbol for a value you don't want to store, if
not, you'll have to add some logic to map that to a valid instance
variable name.)

···

On Sat, Mar 14, 2009 at 4:47 PM, Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

Christopher Dicely wrote:

Ruby blocks are closures

That was not my understanding. How does one return a block from a function? (That's not a rhetorical question; I'm eager to learn.)

Christopher Dicely wrote:

matt neuburg wrote:

I am trying to set a group of variables to values stored in a tab
delimited string. I have no problem splitting the string into an array,
call it e, or in putting the variable names in a second array, call it
v. The problem arises when I try to merge the two together. If I enter:
irb(main):073:0> p v
["name", "street", "city", "state", "zip", "*", "telephone"]
=> nil
irb(main):074:0> p e
["John Doe", "123 Main St", "Anywhere", "US", "01234-5678", "ab123",
"1234567890"]
=> nil
irb(main):075:0> eval "#{v[0]} = e[0]"
=> "John Doe"
irb(main):076:0> p name
"John Doe"
=> nil

it works as I want. But when I try:
irb(main):077:0> v.each_index {|i| eval "#{v[i]} = e[0]"}
=> ["name", "street", "city", "state", "zip", "*", "telephone"]

and then enter: puts zip
it returns with a NameError saying that the variable was undefined.

Inside a block, local variables are local to the block. So, you're
creating all those local variables and then the block ends and they are
all thrown away.

I can see that if I create the variable before running the method the
variable is set even after the method ends. Which of course brings up the
question of why is the change visible outside of the block?

Ruby blocks are closures, and they therefore capture the environment
where they are created. So if a local variable exists in that
environment when the block is created, the block has access to it, and
changes will affect the externally-created local variable. But new
local variables introduced in the block are local to the block. In
essence, the environment of the block is subordinate to the
environment in which it is created.

This is what is confusing to me. If I use a variable in a function and then call another function which uses the same name C does not change the first instance when I return from the second function unless it was part of the call. In Basic if I make a change or create a new variable it is visible everywhere afterwards. Ruby seems to be doing something like a mix of the two where if the variable is undefined entering the block it stays undefined outside the block, but if it was defined before entering the block it is changed to the new value on exit.

Why do you need variables called "name" (etc.) anyway? Why not use a
hash, so that h["name"] contains the name ("John Doe"), h["street"]
contains the street, and so on? So:

I have been programming in Business Basic for over 20 years and am used to
reading a file with a statement like:
Read(1,Key="1234")Name$,Street$,City$,State$,Zip$,*,Telephone$
and then using those variables.

Assuming you have a "keyed_read" method defined properly, you can easily do:

name, street, city, state, zip, _, telephone =
data_file.keyed_read(:key=>"1234")

But the thing that is harder is dynamically assigning variable names
based on a array provided. Using hashes or structs are the more
idiomatic Ruby ways of doing something similar to that. Its possible
to do something very close to what you were attempting with instance
variables using Object#instance_variable_set.

Part of what brought this up is that I work with a lot of Excel spreadsheets of information from various clients. I export the data into tab separated records. I then break up the string and assign it to the various variables for further processing. Currently in Business Basic I have to find the position of the first tab, set the first variable to the data up to that position, chop off the first part of the string up to the tab and repeat. In Ruby it looks like I can just split the string and combine it with the variable names in a couple of steps.

irb(main):036:0> v.zip(e).each { |var, val|
irb(main):037:1* begin
irb(main):038:2* self.instance_variable_set(("@"+var).to_sym,val)
irb(main):039:2> rescue
irb(main):040:2> end
irb(main):041:1> }
=> [["name", "John Doe"], ["street", "123 Main St"], ["city",
"Anywhere"], ["state", "US"], ["zip", "01234-5678"], ["*", "ab123"],
["telephone", "1234567890"]]
irb(main):042:0> @name
=> "John Doe"

I will play with this and see how it compares to the other suggestions from the group.

···

On Sat, Mar 14, 2009 at 4:47 PM, Michael W. Ryder > <_mwryder@worldnet.att.net> wrote:

Michael W. Ryder <_mwryder@worldnet.att.net> wrote:

So if you want to do it that way, you can. (Note, I'm assuming that
the '*' is a special symbol for a value you don't want to store, if
not, you'll have to add some logic to map that to a valid instance
variable name.)

While block are closures, they aren't first-class. You can't return a
block, because its not a normal Ruby object. You can return an
instance of class Proc built from a particular block, though, and use
it outside the method in which it was created.

For example:

irb(main):001:0> def counter(seed=0)
irb(main):002:1> lambda { seed += 1 }
irb(main):003:1> end
=> nil
irb(main):004:0> c = counter(0)
=> #<Proc:0x02bcfaf0@(irb):2>
irb(main):005:0> c.call
=> 1
irb(main):006:0> c.call
=> 2

···

On Sun, Mar 15, 2009 at 10:27 AM, Jeff Schwab <jeff@schwabcenter.com> wrote:

Christopher Dicely wrote:

Ruby blocks are closures

That was not my understanding. How does one return a block from a function?
(That's not a rhetorical question; I'm eager to learn.)