Extract hash into local variables?

C

csn

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
 
R

Robert Klemme

csn said:
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.,

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
 
S

Scott

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 :) 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
 
S

Scott

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

Robert Klemme

Jeffrey Schwab said:
csn said:
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

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
 
C

csn

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
 
C

csn

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
 
R

Ross Bamford

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

Robert Klemme

2006/2/4 said:
Robert said:
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 !=3D Perl && Ruby !=3D 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
 
G

Gene Tani

Jeffrey said:
Robert said:
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.
 
J

Jim Weirich

Jeffrey said:
Robert said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,662
Latest member
sxarexu

Latest Threads

Top