Binding question

I have this example codes:

b = binding
['x', 'y'].each { |e| eval("#{e} = 123", b) }
p local_variables
p x
p y

Run the codes from irb, everything works.
However, save it to a file like pgm.rb and ruby it as "ruby pgm.rb", I
got the error message as:
["b", "x", "y"] #==> it does print the local_variables correctely.
pgm.rb:4: undefined local variable or method 'x' for main:Object (NameError)

Why? local_variables array does show me the "x", why it fail on "p x" ??

"name =" creates a local variable in Ruby, and you have defined no method 'x'. They may also exist in the binding, but Ruby doesn't allow you to get at them that way.

eval("#{e} = 123", binding) looks ugly, so you should expect strange things to happen.

···

On 08 Dec 2004, at 13:44, email55555 email55555 wrote:

I have this example codes:

b = binding
['x', 'y'].each { |e| eval("#{e} = 123", b) }
p local_variables
p x
p y

Run the codes from irb, everything works.
However, save it to a file like pgm.rb and ruby it as "ruby pgm.rb", I
got the error message as:
["b", "x", "y"] #==> it does print the local_variables correctely.
pgm.rb:4: undefined local variable or method 'x' for main:Object (NameError)

Why? local_variables array does show me the "x", why it fail on "p x" ??

Hi Eric,

I know it is ugly. But:

(1) Why it works on irb and not on "ruby pgm.rb" ?

(2) What is the correct way to "dynamically" create local variable ?
For example: how to create local variables from an given local
vairable name strings array ?

Thanks.

email55555 email55555 wrote:

I have this example codes:

b = binding
['x', 'y'].each { |e| eval("#{e} = 123", b) }
p local_variables
p x
p y

Run the codes from irb, everything works.
However, save it to a file like pgm.rb and ruby it as "ruby pgm.rb", I
got the error message as:
["b", "x", "y"] #==> it does print the local_variables correctely.
pgm.rb:4: undefined local variable or method 'x' for main:Object (NameError)

Why? local_variables array does show me the "x", why it fail on "p x" ??

Because ruby determines at compile time if `x' should be treated as a method call or as a variable lookup.
If previously in the code there was an assignment to `x', then ruby considers it a variable. But at compile time it can not eval the string, so it thinks that `x' is a method.

Hi Eric,

I know it is ugly. But:

(1) Why it works on irb and not on "ruby pgm.rb" ?

irb is written in Ruby, so it is not 100% identical to ruby itself.

(2) What is the correct way to "dynamically" create local variable ?
For example: how to create local variables from an given local
vairable name strings array ?

What are you really trying to do? We may be able to come up with a cleaner way of doing what you want.

···

On 08 Dec 2004, at 14:21, email55555 email55555 wrote:

email55555 email55555 wrote:

I know it is ugly. But:

(1) Why it works on irb and not on "ruby pgm.rb" ?

IRB wraps everything you do in eval(). It also does a bunch of other magic to make everything work correctly in common cases.

(2) What is the correct way to "dynamically" create local variable ?
For example: how to create local variables from an given local
vairable name strings array ?

The problem is that Ruby decides on compile time whether 'a' refers to the variable 'a' (if it has seen an assignment to it earlier) or whether it is the method call 'a()'.

A solution would be to set the variable to nil before you use it, but I think it is generally a bad idea to assign to variables of your caller.

Are you sure you can not use a Hash instead? Also note that methods in Ruby can trivially return multiple values like this:

def plus_minus(a, b)
   return(a + b, a - b)
end

sum, sub = plus_minus(1, 3)
sum # => 4
sub # => -2

I am learning Tk from the book "Practical Programming in Tcl and Tk".
I try to translate the book's examples from Tcl to Ruby. (as exercice
for myself)

Here is one of kind example: (TCL)

foreach name {one two three four five} {
  label .$name -text $name -bg white
  pack .$name -side top
}
pack .five -before .one

Ruby version: (Of course, there are many ways to write it)

%w[one two three four five].each {|name| ??? =
TkLabel.new(:text=>name, :bg=>'white') { pack(:side=>:top) } }
Tk.pack five, :before=>one ## trouble... "five", "one" are not local variables.

As you can see, the problem is how can I create local variable one,
two ... five
to correspond the 5 TkLabels immediately (dynamically)? ( what to
replace the ??? )

So, I came out by using eval binding to force automatically create
local variables base on it name.
Unfortunately, it does not work.

So, what's your suggestion? Do not using array ? write them one by one ?
or ...

variables = %w[one two three four five].map {|e|
TkLabel.new(:text=>name, :bg=>'white') {pack(:side=>:top)}}
Tk.pack variable[4] :before=>variables[0]

email55555 email55555 wrote:

I am learning Tk from the book "Practical Programming in Tcl and Tk".
I try to translate the book's examples from Tcl to Ruby. (as exercice
for myself)

Here is one of kind example: (TCL)

foreach name {one two three four five} {
  label .$name -text $name -bg white
  pack .$name -side top
}
pack .five -before .one

[...]
So, what's your suggestion? Do not using array ? write them one by one ?
or ...

variables = %w[one two three four five].map {|e|
TkLabel.new(:text=>name, :bg=>'white') {pack(:side=>:top)}}
Tk.pack variable[4] :before=>variables[0]

Yes, I'd use an Array like in your sample. Tk is a bit more chaotic in this matter which is probably why they are using variables instead.

You could also use a Hash like this:

labels = Hash.new
%w[one two three four five].each do |name|
   label = TkLabel.new(:text => name, :bg => 'white') do
     pack(:side=>:top)
   end
   labels[name] = label
end

Tk.pack(label["four"], :before => label["one"])

Or even (Warning: Typed directly into the mail)

labels = %w[one two three four five].inject({}) { | result, name |
  result[name] = TkLabel.new(:text => name, :bg => 'white') do
    pack(:side => :top)
  end
  result
}

Tk.pack(label["four"], :before => label["one"])

regards,

Brian

···

On Thu, 9 Dec 2004 10:02:30 +0900 Florian Gross <flgr@ccan.de> wrote:

email55555 email55555 wrote:

> I am learning Tk from the book "Practical Programming in Tcl and Tk".
> I try to translate the book's examples from Tcl to Ruby. (as exercice
> for myself)
>
> Here is one of kind example: (TCL)
>
> foreach name {one two three four five} {
> label .$name -text $name -bg white
> pack .$name -side top
> }
> pack .five -before .one
>
> [...]
> So, what's your suggestion? Do not using array ? write them one by one ?
> or ...
>
> variables = %w[one two three four five].map {|e|
> TkLabel.new(:text=>name, :bg=>'white') {pack(:side=>:top)}}
> Tk.pack variable[4] :before=>variables[0]

Yes, I'd use an Array like in your sample. Tk is a bit more chaotic in
this matter which is probably why they are using variables instead.

You could also use a Hash like this:

labels = Hash.new
%w[one two three four five].each do |name|
   label = TkLabel.new(:text => name, :bg => 'white') do
     pack(:side=>:top)
   end
   labels[name] = label
end

Tk.pack(label["four"], :before => label["one"])

--
Brian Schröder
http://www.brian-schroeder.de/

Brian Schröder wrote:

labels = %w[one two three four five].inject({}) { | result, name |
  result[name] = TkLabel.new(:text => name, :bg => 'white') do pack(:side => :top)
  end
  result
}

I'm always a bit skeptic about using .inject with in-place operations -- it seems to defy its purpose. Sure, it makes code a bit shorter, but is it really worth that? I'm not certain.

Thats obviously a matter of taste. I like this code, because I'm injecting a
seed and a lambda into the array thereby transforming it into something else.
To me that is what inject is all about. I don't see a fundamental difference
between the above and

(0..10).inject(0) { | r, v | r + v }

best regards,

Brian

···

On Thu, 9 Dec 2004 22:52:28 +0900 Florian Gross <flgr@ccan.de> wrote:

Brian Schröder wrote:

> labels = %w[one two three four five].inject({}) { | result, name |
> result[name] = TkLabel.new(:text => name, :bg => 'white') do
> pack(:side => :top)
> end
> result
> }

I'm always a bit skeptic about using .inject with in-place operations --
it seems to defy its purpose. Sure, it makes code a bit shorter, but is
it really worth that? I'm not certain.

--
Brian Schröder
http://www.brian-schroeder.de/