Extract hash into local variables?

Is there a Ruby function similar to PHP's extract/list? What I'd like to
do is:

def foo(args={a=>1, b=>2, c=>3})
  args.extract

  puts a
  puts b
  puts c
end

Thanks
csn

···

--
Posted via http://www.ruby-forum.com/.

Maybe args.inspect

···

2006/2/4, csn <cool_screen_name90001@yahoo.com>:

Is there a Ruby function similar to PHP's extract/list? What I'd like to
do is:

def foo(args={a=>1, b=>2, c=>3})
  args.extract

  puts a
  puts b
  puts c
end

Thanks
csn

--
Posted via http://www.ruby-forum.com/\.

You cannot do that as local variables have to be declared in the code. You'll have to do that by hand, i.e.,

def foo(args={:a=>1, :b=>2, :c=>3})
  a = args[:a] || 1
  b = args[:b] || 2
  a = args[:c] || 3

  puts a,b,c
end

or

def foo(args={})
  args = args.merge(:a=>1, :b=>2, :c=>3)
  a = args[:a]
  b = args[:b]
  a = args[:c]

  puts a,b,c
end

What do you need that for? Maybe there is a better solution.

Kind regards

    robert

···

csn <cool_screen_name90001@yahoo.com> wrote:

Is there a Ruby function similar to PHP's extract/list? What I'd like
to do is:

def foo(args={a=>1, b=>2, c=>3})
args.extract

puts a
puts b
puts c
end

Thanks
csn

This is a weak solution, but:

args.each_pair { |k, v| instance_variable_set("@#{k}", v) }

will create instance variables for each key in the hash and set them to
the associated value. There may be a way to extend Hash with an
extract method, but I'm not thinking right now so :slight_smile: Calling
instance_variable_set will create the variable within the scope of the
Hash, so it wont be usable where you actually want it.

Another solution could be to use OpenStruct:

def foo(args={:a=>1, :b=>2, :c=>3})
  args = OpenStruct.new(args)

  puts args.a
  puts args.b
  puts args.c
end

Hope that helps a little,
Scott

csn wrote:

Is there a Ruby function similar to PHP's extract/list? What I'd like to do is:

def foo(args={a=>1, b=>2, c=>3})
  args.extract

  puts a
  puts b
  puts c
end

I'm kind of disturbed by how hard this actually seems. There does not seem to be any decent way to modify the set of local variables through introspection. Here's a weak work-around:

def foo(args={:a=>1, :b=>2, :c=>3})
   @args = args

   def method_missing(m)
     @args[m]
   end

   puts a
   puts b
   puts c

end

foo

Robert Klemme wrote:

···

csn <cool_screen_name90001@yahoo.com> wrote:
> Is there a Ruby function similar to PHP's extract/list? What I'd like
> to do is:
>
> def foo(args={a=>1, b=>2, c=>3})
> args.extract
>
> puts a
> puts b
> puts c
> end
>
> Thanks
> csn

You cannot do that as local variables have to be declared in the code.
You'll have to do that by hand, i.e.,

Robert's correct in the general case, but you can read a hash and have
locals that can be accessed through eval:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/159842

But probably, like he said, you want to refactor

Duh... yea, should have read the previously posted link,
local_variable_set does make quite a bit more sense!

Very weak. This is by no means thread safe. Also it will keep references to the arguments around after the method terminates. Plus, you're redefining method_missing all the time. Too many drawbacks IMHO.

I don't know why people strive to get local variables set dynamically. If you want to use them, you'll have to make them explicitely anyway. So what do we gain? Is there a real world problem that can't be solved without this?

Cheers

robert

···

Jeffrey Schwab <jeff@schwabcenter.com> wrote:

csn wrote:

Is there a Ruby function similar to PHP's extract/list? What I'd
like to do is:

def foo(args={a=>1, b=>2, c=>3})
  args.extract

  puts a
  puts b
  puts c
end

I'm kind of disturbed by how hard this actually seems. There does not
seem to be any decent way to modify the set of local variables through
introspection. Here's a weak work-around:

def foo(args={:a=>1, :b=>2, :c=>3})
  @args = args

  def method_missing(m)
    @args[m]
  end

  puts a
  puts b
  puts c

end

Thanks for the replies. I also found this bit of code:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/41812

Which is similar to Scott's 'instance_variable_set' above:

def extract(hash)
  hash.each do |key,value|
     eval "$#{key} = #{value.inspect}"
  end
end

Maybe it's possible to rewrite this into something inside of a function
which only sets local variables:

def foo(args=>{:a=>1, :b=>2, :c=>3})
  for key, value in args # or for args[0..args.length] ?
    eval "#{key} = #{value.inspect}"
  end

  puts a, b, c
end

I'm surprised there's not an easy and simple way too ;(. I read in
Programming Ruby that named arguments are planned for Ruby 2.0.

csn

···

--
Posted via http://www.ruby-forum.com/.

Robert Klemme wrote:

I don't know why people strive to get local variables set dynamically. If you want to use them, you'll have to make them explicitely anyway. So what do we gain? Is there a real world problem that can't be solved without this?

Of course not. But some code is a lot cleaner if you can use the local symbol table as a hash. This is a common technique in Perl, and it seems to be in Python (via [gs]etattr and globals()) as well.

I came up with this:

def foo(args = {'a'=>1, 'b'=>2, 'c'=>3})
  a,b,c = args.values_at('a', 'b', 'c')

  puts a,b,c

  a,b,c = args.values

  puts a,b,c
end

def foo2(args = {:a=>1, :b=>2, :c=>3})
  a,b,c = args.values

  puts a,b,c
end

foo outputs:
1
2
3
1
2
3

foo2 outputs:
2
3
1

csn

···

--
Posted via http://www.ruby-forum.com/.

Careful with that, I think hash order is undefined?

I was playing a bit with this stuff recently and came up with this:

  class Method
    def arg_names
      catch(:traced) do
        dummy_args = [nil]
        dummy_args *= self.arity unless self.arity < 0

        set_trace_func lambda { |event, file, line, id, bdng, cls|
          if event == 'call'
            set_trace_func nil
            throw(:traced, eval('local_variables',bdng).map { |lv| lv.intern })
          end
        }

        self.call(*dummy_args)

        # if we get here, call is bust
        set_trace_func nil
        raise "No ruby method call in block"
      end
    end

    def hcall(hash, &blk)
      names = arg_names.reject { |n| n =~ /^(.*proc|bl(oc)?k)$/ }
      call(*names.map { |n| hash[n] or raise "Missing argument" }, &blk)
    end
  end

  def foo(a,b,c)
    p [a,b,c]
  end

  method(:foo).hcall(:c => 'C', :a => 'A', :b => 'B')
  # => ["A", "B", "C"]

but it's block argument handling is very weak and it can't handle methods implemented in C. Better to just wait for Ruby 2.0. Personally, I've never once missed them anyway.

···

On Sat, 04 Feb 2006 19:46:03 -0000, csn <cool_screen_name90001@yahoo.com> wrote:

def foo2(args = {:a=>1, :b=>2, :c=>3})
  a,b,c = args.values

  puts a,b,c
end

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Ruby != Perl && Ruby != Python

I'd rather discuss this with a real world example that demonstrates
where this idiom is really needed or at least superior in clarity. ATM
I fail to see where this would bring real benefits.

Cheers

robert

···

2006/2/4, Jeffrey Schwab <jeff@schwabcenter.com>:

Robert Klemme wrote:
> I don't know why people strive to get local variables set dynamically.
> If you want to use them, you'll have to make them explicitely anyway.
> So what do we gain? Is there a real world problem that can't be solved
> without this?

Of course not. But some code is a lot cleaner if you can use the local
symbol table as a hash. This is a common technique in Perl, and it
seems to be in Python (via [gs]etattr and globals()) as well.

--
Have a look: Robert K. | Flickr

Jeffrey Schwab wrote:

Robert Klemme wrote:
> I don't know why people strive to get local variables set dynamically.
> If you want to use them, you'll have to make them explicitely anyway.
> So what do we gain? Is there a real world problem that can't be solved
> without this?

Of course not. But some code is a lot cleaner if you can use the local
symbol table as a hash. This is a common technique in Perl, and it
seems to be in Python (via [gs]etattr and globals()) as well.

I think on this specific point you're wrong about python. Writing to
globals dict will change the global namespace, writing to locals dict
won't have any effect (it's often described as read-only, but that's
not quite correct)

Maybe we're arguing about passing keywords args to a method, in which
case you can look at Gavin Sinclair's solution in the draft Ruby
cookbook.

Jeffrey Schwab <jeff@schwabcenter.com> writes:

Robert Klemme wrote:

I don't know why people strive to get local variables set
dynamically. If you want to use them, you'll have to make them
explicitely anyway. So what do we gain? Is there a real world
problem that can't be solved without this?

Of course not. But some code is a lot cleaner if you can use the
local symbol table as a hash. This is a common technique in Perl, and
it seems to be in Python (via [gs]etattr and globals()) as well.

If that's true, consider it a good reason *not to*. :wink:

Local variables are local variables and not meant to be syntactic
shortcuts for anything else.

···

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Jeffrey Schwab wrote:

Robert Klemme wrote:

I don't know why people strive to get local variables set dynamically.
If you want to use them, you'll have to make them explicitely anyway.
So what do we gain? Is there a real world problem that can't be solved
without this?

Of course not. But some code is a lot cleaner if you can use the local
symbol table as a hash. This is a common technique in Perl, and it
seems to be in Python (via [gs]etattr and globals()) as well.

It may be common in Perl, but it is just as misguided there as well.
Read Why it's stupid to `use a variable as a variable name' for details.

···

--
-- Jim Weirich

--
Posted via http://www.ruby-forum.com/\.

Gene Tani wrote:

Maybe we're arguing about passing keywords args to a method, in which
case you can look at Gavin Sinclair's solution in the draft Ruby
cookbook.

it's here too