Adding local variables via block


#1

Hi,

i’m a roob noob and I’m trying to grasp procs/blocks/etc.

specifically, I’d like to pass a piece of code into a method.

the piece of code will contain a variable name and an assignment

e.g.

####################

def lamb_test

cond = 'hello'

yield

puts cond + ' ' + name

end

lamb_test {name = ‘bob’}

###################

When I do this, I get the following error:

"undefined local variable or method `name’ for main:Object…

is it possible to define a local variable like this on the fly with
blocks?

obviously, this is an oversimplified case but I want to be able to
dynamically insert a local variable and an assignment to that variable
into the “generic” function lamb_test

Is there a better way?

TIA!


#2

On 2/20/07, Paul D. removed_email_address@domain.invalid wrote:

the piece of code will contain a variable name and an assignment

###################
blocks?

obviously, this is an oversimplified case but I want to be able to
dynamically insert a local variable and an assignment to that variable
into the “generic” function lamb_test

Is there a better way?

yield will return the value from the block it executes. Just have your
block return the name ‘bob’ …

def lamb_test
cond = ‘hello’
name = yield
puts cond + ’ ’ + name
end

lamb_test { ‘bob’ } #=> ‘hello bob’

In the more general case, you would need to use one of the ‘eval’
methods in order to create a new local variable in a local namespace.
However, (1) the ‘eval’ methods work only on strings and not blocks,
and (2) they are mostly evil and should be handled with great care.

Blessings,
TwP


#3

Hi,

On Tuesday 20 February 2007 19:33, Paul D. wrote:

####################
def lamb_test
cond = ‘hello’
yield
puts cond + ’ ’ + name
end

lamb_test {name = ‘bob’}

###################

the local variables defined in the block live only in the block’s scope.
if a
local variable is defined out of the block, a closure is made and you
can
access (and redefine) that variable in the block:

a = 1
=> 1

p = proc { a = 2; b = 3 }
=> #Proc:0xb7ae7150@:8(irb)

p.call
=> 2

a
=> 2

b
=> NameError: …

Is there a better way?

well

def lamb_test
cond = ‘hello’
name = yield
puts cond + ’ ’ + name
end

better:

def lamb_test
cond = hello
puts cond + ’ ’ + yield
end

better:

def lamb_test
puts [‘hello’, yield].join(’ ')
end

even better:

def lamb_test
[‘hello’, yield].join(’ ')
end

puts lamb_test { ‘world’ }

if “puts” is not strictly necessary in your method, take it out, and
just
return a string. this way, when you need to print that string on
something
different than a text terminal, you don’t need to mess with the class
you’ve
written earlier.

HTH! :slight_smile:


#4

On Feb 20, 2007, at 12:52 PM, Tim P. wrote:

In the more general case, you would need to use one of the ‘eval’
methods in order to create a new local variable in a local namespace.
However, (1) the ‘eval’ methods work only on strings and not blocks,
and (2) they are mostly evil and should be handled with great care.

eval() introduces a new scope, so local variables created in that
context do not leak into the calling scope:

def test_eval
eval(“name = ‘James’”, binding)
name
end
=> nil

tes
test test_eval

test_eval
NameError: undefined local variable or method name' for main:Object from (irb):3:intest_eval’
from (irb):5
from :0

James Edward G. II


#5

Hi –

On Wed, 21 Feb 2007, Tim P. wrote:

In the more general case, you would need to use one of the ‘eval’
methods in order to create a new local variable in a local namespace.
However, (1) the ‘eval’ methods work only on strings and not blocks,
and (2) they are mostly evil and should be handled with great care.

Actually the only one that works only on strings is eval.
instance_eval and class_eval work on blocks too.

David


#6

On 2/20/07, James Edward G. II removed_email_address@domain.invalid wrote:

eval() introduces a new scope, so local variables created in that
context do not leak into the calling scope:

I am not recommending this, but you can force a binding into an eval.
However, due to how the Ruby parser identifies local variables, they
need to be referenced in “code” before you can use them normally.

def foo(binding)
eval(“x = 42”, binding)
end

foo(binding)
x = x
puts “x = #{x}”

Good luck
pth


#7

Hi,

On Tuesday 20 February 2007 21:02, Patrick H. wrote:

I am not recommending this, but you can force a binding into an eval.
However, due to how the Ruby parser identifies local variables, they
need to be referenced in “code” before you can use them normally.

my irb disagrees:

vjt@neutrino:~$ irb
/home/vjt| irb(main):001:0> eval ‘x = 42’, binding
42
/home/vjt| irb(main):002:0> x
42
/home/vjt| irb(main):003:0> RUBY_VERSION
“1.8.5”
/home/vjt| irb(main):004:0>


#8

On Wed, 21 Feb 2007, Marcello B. wrote:

/home/vjt| irb(main):001:0> eval ‘x = 42’, binding
42
/home/vjt| irb(main):002:0> x
42
/home/vjt| irb(main):003:0> RUBY_VERSION
“1.8.5”
/home/vjt| irb(main):004:0>

try writing that in a script. in irb you are already in eval.
google the
archives for more info.

cheers.

-a


#9

Hi,

On Tuesday 20 February 2007 21:14, removed_email_address@domain.invalid wrote:

vjt@neutrino:~$ irb
/home/vjt| irb(main):001:0> eval ‘x = 42’, binding
42
/home/vjt| irb(main):002:0> x
42
/home/vjt| irb(main):003:0> RUBY_VERSION
“1.8.5”
/home/vjt| irb(main):004:0>

try writing that in a script.

vjt@neutrino:~$ ruby -e “eval(‘x=42’,binding); x”
-e:1: undefined local variable or method `x’ for main:Object (NameError)

in irb you are already in eval. google the archives for more info.

gotcha! :slight_smile: thanks