Forum: Ruby Extract hash into local variables?

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.
csn (Guest)
on 2006-02-04 12:53
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
Bruno C. (Guest)
on 2006-02-04 13:53
(Received via mailing list)
Maybe args.inspect

2006/2/4, csn <removed_email_address@domain.invalid>:
Robert K. (Guest)
on 2006-02-04 14:43
(Received via mailing list)
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
Gene T. (Guest)
on 2006-02-04 16:21
(Received via mailing list)
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/...

But probably, like he said, you want to refactor
Scott (Guest)
on 2006-02-04 20:40
(Received via mailing list)
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
Scott (Guest)
on 2006-02-04 20:47
(Received via mailing list)
Duh... yea, should have read the previously posted link,
local_variable_set does make quite a bit more sense!
Jeffrey S. (Guest)
on 2006-02-04 20:47
(Received via mailing list)
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
csn (Guest)
on 2006-02-04 21:10
Thanks for the replies. I also found this bit of code:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...

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
csn (Guest)
on 2006-02-04 21:45
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
Robert K. (Guest)
on 2006-02-04 22:14
(Received via mailing list)
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
Jeffrey S. (Guest)
on 2006-02-04 22:14
(Received via mailing list)
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.
Ross B. (Guest)
on 2006-02-04 23:19
(Received via mailing list)
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.
Robert K. (Guest)
on 2006-02-04 23:46
(Received via mailing list)
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
Gene T. (Guest)
on 2006-02-05 03:15
(Received via mailing list)
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.
Gene T. (Guest)
on 2006-02-05 03:15
(Received via mailing list)
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
Christian N. (Guest)
on 2006-02-05 13:45
(Received via mailing list)
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*. ;-)

Local variables are local variables and not meant to be syntactic
shortcuts for anything else.
Jim W. (Guest)
on 2006-02-05 14:58
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.
This topic is locked and can not be replied to.