简体   繁体   中英

Ruby: How to map a function to a hash

I can't figure out how to assign a function call to a ruby hash. What I want to do is to assign a function to a hash key, and later call this function using the classic hash lookout syntax.

def Foo()
  puts "bar"
end

puts "Assigning"
test = { "foo" => Foo() }

puts "Executing"
test["foo"]

This code fails, function Foo is called after puts "Assign" , during hash creation, and nothing happens after puts "Executing"

def Foo()
  puts "bar"
end

puts "Assigning"
test = { "foo" => Foo }

puts "Executing"
test["foo"]

with this code I receive an uninitialized constant Foo (NameError) .

Finally with

def Foo()
  puts "bar"
end

puts "Assigning"
test = { "foo" => :Foo }

puts "Executing"
test["foo"]

I get not outputs.

Any suggestions?

Thanks to all for answres and suggestions.

What I'm going to do is to test if a hash based approach to call function is faster than an equivalent code based on if / case statements.

funcs["foo"].call

fatser than

if func_name == "foo" then
  Foo()
elsif ...
...
end

or

case func_name
when "foo"
  Foo()
when ...
  ...
end

Obviously for a big number of functions (~150) and hundreds of calling cycles

you could use lambda's instead of methods. Two options here:

hash = {:foo => lambda { puts 'bar } }

hash[:foo].call

the second (more complicated) is this:

irb(main):001:0> class Hash
irb(main):002:1>   alias :orig_anc :'[]'
irb(main):003:1>
irb(main):004:1*   def [](key)
irb(main):005:2>     if orig_anc(key).is_a? Proc
irb(main):006:3>       orig_anc(key).call
irb(main):007:3>     else
irb(main):008:3*       orig_anc(key)
irb(main):009:3>     end
irb(main):010:2>   end
irb(main):011:1> end
=> nil
irb(main):012:0> h = {:hello => 'world', :foo => lambda { puts 'bar' }}
=> {:hello=>"world", :foo=>#<Proc:0x843224c@(irb):12 (lambda)>}
irb(main):013:0> h[:hello]
=> "world"
irb(main):014:0> h[:foo]
bar
=> nil
irb(main):015:0>

The second one just allows you to skip using 'call' method

There is no easy possibility to make your function execute simply by retrieving the hash key withput overriding Hash 's [] method, as Vlad pointed out, ie

def foo
  puts "hi"
end

... # magic

test["foo"] # outputs hi

won't work. What you can do, though, is assign the method reference using Object#method and then invoke it using call :

def foo
  puts "hi"
end

test = { "foo" => method(:foo) }

test["foo"].call # => hi

First things first, start method names with lower case letters. Then, moving to first example, Ruby is eager so test = { "foo" => Foo() } Foo is called.

In the second example, variables starting with an uppercase letter are considered constants, so Ruby looks for one and do not bother looking for a method. Take into account that if foo where a method then foo would be called as in the first example.

Third example: test["foo"] returns:Foo, a symbol. Nowhere in your code Foo() is invoked.

* Suggestions

1.- Take a look at identifiers nomenclature in Ruby.

2.- You may took a look at lambda , Proc and Object#method

3.- If you have some money to spare and don't mind buying a pair of books, give Programming Ruby and Metaprogramming in Ruby a chance (both can be bought in PragProg .

You could try using the hash constructor with a block, so

def foo
  puts "hi"
end

test = Hash.new do |hash, key|
  send(key) if respond_to?(key)
end

test["foo"] # prints "hi", returns nil

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM