Any way to manipulate variables and bindings?

def foo
    bar [:a, :b, :c]
    a
    b
    c
end

I would like a, b, and c to be local variables with values 1, 2, 3
(positions of :a, :b, :c). Is there some implementation of
    def bar (symbol_list)
        symbol_list.each_with_index { |s, i|
            ???
        }
    end
that will do this?

Not really. Technically, you could do something like this:

def bar(&block)
  ary = block.call
  ary.each_with_index{|s,i| eval("#{s}=#{i}", block.binding)}
end
    ==>nil
bar{[:a,:b,:c]}
    ==>[:a, :b, :c]
[a,b,c]
    ==>[0, 1, 2]

This would create local variables with those values. However, they
wouldn't be accessible in anything other than irb:

mark@eMac% ruby
def bar(&block)
  ary = block.call
  ary.each_with_index{|s,i| eval("#{s}=#{i}", block.binding)}
end
bar{[:a,:b,:c]}
p [a,b,c]
-:6: undefined local variable or method `a' for main:Object (NameError)
mark@eMac%

This is because ruby decides at _compile time_ whether an identifier
represents a variable or a method call. Since Ruby doesn't see
anything being assigned to a b and c at compile time, it assumes they
are methods, and raises an error.

You could hack around this by pre-defining your variables, but that's ugly.

mark@eMac% ruby
def bar(&block)
  ary = block.call
  ary.each_with_index{|s,i| eval("#{s}=#{i}", block.binding)}
end
a,b,c = *
bar{[:a,:b,:c]}
p [a,b,c]
[0, 1, 2]
mark@eMac%

HTH,
Mark

···

On Fri, 3 Dec 2004 03:27:45 +0900, itsme213 <itsme213@hotmail.com> wrote:

def foo
    bar [:a, :b, :c]
    a
    b
    c
end

I would like a, b, and c to be local variables with values 1, 2, 3
(positions of :a, :b, :c). Is there some implementation of
    def bar (symbol_list)
        symbol_list.each_with_index { |s, i|
            ???
        }
    end
that will do this?

itsme213 wrote:

def foo
    bar [:a, :b, :c]
    a
    b
    c
end

I would like a, b, and c to be local variables with values 1, 2, 3
(positions of :a, :b, :c). Is there some implementation of
    def bar (symbol_list)
        symbol_list.each_with_index { |s, i|
            ???
        }
    end
that will do this?

You need the binding of the caller. Then you can just do eval("#{symbol} = value", context) where context is the binding.

I have attached binding_of_caller.rb -- it provides Binding.of_caller which yields the binding of the caller. See the documentation, it has to be used in a relatively odd way:

def inc(name)
   Binding.of_caller do |context|
     eval("#{name} += 1")
   end
end

x = 0; inc(:x); x # => 1

I have also attached variable.rb -- this provides variable boxing via Variable[:foo] which creates an object for that variable. You can do var.value = 5 and so on. Note that this does not yet work with instance and class variables. It can be used like this:

def inc(variable)
   variable.value += 1
end

x = 0; inc(Variable[:x]); x # => 1

It would also be interesting to know what you are using this for.

binding_of_caller.rb (2.56 KB)

variable.rb (954 Bytes)

"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:I7Jrd.100267$jq5.22882@fe2.texas.rr.com...

def foo
    bar [:a, :b, :c]
    a
    b
    c
end

I would like a, b, and c to be local variables with values 1, 2, 3
(positions of :a, :b, :c). Is there some implementation of
    def bar (symbol_list)
        symbol_list.each_with_index { |s, i|
            ???
        }
    end
that will do this?

As Mark pointed out, you can't get this to work because of the compile
time decisions Ruby takes. You can either do this

def bar(*a) 0...a.size end

def foo
  a,b,c = *bar(:a,:b,:c)
  puts a,b,c
end

or this

def bar2(b,*a)
  a.each_with_index {|s,i| eval("#{s}=#{i}",b)}
end

def foo2
  bind = binding
  bar2(bind, :a,:b,:c)
  [:a,:b,:c].each do |sym|
    puts eval(sym.to_s, bind)
  end
end

which is really clumsy.

Kind regards

    robert

I'm exploring some domain-specific language capabilities in Ruby. One of the
things I want to do appears doable by generating local variables and/or
methods in the context of the caller.

···

"Florian Gross" <flgr@ccan.de> wrote

You need the binding of the caller. Then you can just do eval("#{symbol}
= value", context) where context is the binding.
...
...<stuff i won't pretend to understand but will attempt to use!>
...
It would also be interesting to know what you are using this for.

I should clarify: Ruby will set the variables, they are just
inaccessible unless you assign to them at some point in that binding's
context:

mark@eMac% ruby
eval "a = 23"
p a
^D
-:2: undefined local variable or method `a' for main:Object (NameError)

mark@eMac% ruby
eval "a = 23"
a += 19
p a
^D
42

So, as long as you assign to them anywhere in that context, it's okay.
even if it's after they have been set once. head.spin(:rapidly)

I think the reason for this is that there was a tradeoff made: to do
what you want to do would require that variables be checked at
runtime, not eval-time. This would prevent early detection of errors
related to variable names. I guess it was decided that the error
detection was more important than a slightly more flexible/dynamic
local variable setup.

cheers,
Mark

···

On Fri, 3 Dec 2004 21:42:47 +0900, Robert Klemme <bob.news@gmx.net> wrote:

"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:I7Jrd.100267$jq5.22882@fe2.texas.rr.com...

> def foo
> bar [:a, :b, :c]
> a
> b
> c
> end
>
> I would like a, b, and c to be local variables with values 1, 2, 3
> (positions of :a, :b, :c). Is there some implementation of
> def bar (symbol_list)
> symbol_list.each_with_index { |s, i|
> ???
> }
> end
> that will do this?

As Mark pointed out, you can't get this to work because of the compile
time decisions Ruby takes.