From: Christopher Dicely Date: 2009-03-16T02:12:28+09:00 Subject: Re: Problem With eval and each_index On Sat, Mar 14, 2009 at 4:47 PM, Michael W. Ryder <_mwryder@worldnet.att.net> wrote: > matt neuburg 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. >> > > 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.)