Extract hash into local variables?


#1

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


#2

Maybe args.inspect

2006/2/4, csn removed_email_address@domain.invalid:


#3

Robert K. wrote:

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


#4

csn removed_email_address@domain.invalid wrote:

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

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

#5

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


#6

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


#7

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


#8

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


#9

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


#10

Jeffrey S. removed_email_address@domain.invalid wrote:

end
@args[m]
end

puts a
puts b
puts c

end

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


#11

Robert K. 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.


#12

2006/2/4, Jeffrey S. removed_email_address@domain.invalid:

Robert K. 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.

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


#13

On Sat, 04 Feb 2006 19:46:03 -0000, csn
removed_email_address@domain.invalid
wrote:

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

puts a,b,c
end

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.


#14

Jeffrey S. wrote:

Robert K. 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 S.'s solution in the draft Ruby
cookbook.


#15

Gene T. wrote:

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

http://www.rubygarden.org/ruby?KeywordArguments
it’s here too


#16

Jeffrey S. wrote:

Robert K. 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 http://perl.plover.com/varvarname.html for details.


– Jim W.


#17

Jeffrey S. removed_email_address@domain.invalid writes:

Robert K. 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.