binding question

  • Thread starter email55555 email55555
  • Start date
E

email55555 email55555

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" ??
 
E

Eric Hodel

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.
 
E

email55555 email55555

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.
 
E

Eric Hodel

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.
 
F

Florian Gross

email55555 said:
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
 
E

email55555 email55555

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]
 
F

Florian Gross

email55555 said:
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"])
 
C

Carlos

email55555 said:
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.
 
B

Brian Schröder

email55555 said:
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
 
F

Florian Gross

Brian said:
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.
 
B

Brian Schröder

Brian said:
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
 

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,779
Messages
2,569,606
Members
45,239
Latest member
Alex Young

Latest Threads

Top