Forum: Ruby adding local variables via block

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Paul D. (Guest)
on 2007-02-20 20:33
(Received via mailing list)
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!
Tim P. (Guest)
on 2007-02-20 20:52
(Received via mailing list)
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
Marcello B. (Guest)
on 2007-02-20 20:54
(Received via mailing list)
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@(irb):8>
>> 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! :)
James G. (Guest)
on 2007-02-20 21:13
(Received via mailing list)
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:in `test_eval'
         from (irb):5
         from :0

James Edward G. II
Patrick H. (Guest)
on 2007-02-20 22:03
(Received via mailing list)
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
unknown (Guest)
on 2007-02-20 22:11
(Received via mailing list)
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
Marcello B. (Guest)
on 2007-02-20 22:12
(Received via mailing list)
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>
unknown (Guest)
on 2007-02-20 22:14
(Received via mailing list)
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
Marcello B. (Guest)
on 2007-02-20 22:28
(Received via mailing list)
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! :) thanks
This topic is locked and can not be replied to.