可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Previously, I asked about a clever way to execute a method on a given condition "Ruby a clever way to execute a function on a condition."
The solutions and response time was great, though, upon implementation, having a hash of lambdas gets ugly quite quickly. So I started experimenting.
The following code works:
def a()
puts "hello world"
end
some_hash = { 0 => a() }
some_hash[0]
But if I wrap this in a class it stops working:
class A
@a = { 0 => a()}
def a()
puts "hello world"
end
def b()
@a[0]
end
end
d = A.new()
d.b()
I can't see why it should stop working, can anyone suggest how to make it work?
回答1:
that code doesn't work. it executes a
at the time it is added to the hash, not when it is retrieved from the hash (try it in irb).
It doesn't work in the class because there is no a
method defined on the class (you eventually define a method a
on the instance.
Try actually using lambdas like
{0 => lambda { puts "hello world" }}
instead
回答2:
First of all, you are not putting a lambda in the hash. You're putting the result of calling a()
in the current context.
Given this information, consider what the code in your class means. The context of a class definition is the class. So you define an instance method called a
, and assign a class instance variable to the a hash containing the result of calling a
in the current context. The current context is the class A, and class A does not have a class method called a
, so you're trying to put the result of a nonexistent method there. Then in the instance method b
, you try to access an instance variable called @a
-- but there isn't one. The @a
defined in the class context belongs to the class itself, not any particular instance.
So first of all, if you want a lambda, you need to make a lambda. Second, you need to be clear about the difference between a class and an instance of that class.
If you want to make a list of method names to be called on certain conditions, you can do it like this:
class A
def self.conditions() { 0 => :a } end
def a
puts "Hello!"
end
def b(arg)
send self.class.conditions[arg]
end
end
This defines the conditions hash as a method of the class (making it easy to access), and the hash merely contains the name of the method to call rather than a lambda or anything like that. So when you call b(0)
, it send
s itself the message contained in A.conditions[0], which is a
.
回答3:
table = {
:a => 'test',
:b => 12,
:c => lambda { "Hallo" },
:d => def print(); "Hallo in test"; end
}
puts table[:a]
puts table[:b]
puts table[:c].call
puts table[:d].send( :print )
回答4:
If you really just want to pretty this sort of thing up,
why not wrap all your methods in a class like so:
# a container to store all your methods you want to use a hash to access
class MethodHash
alias [] send
def one
puts "I'm one"
end
def two
puts "I'm two"
end
end
x = MethodHash.new
x[:one] # prints "I'm one"
x.two # prints "I'm one"
or, to use your example:
# a general purpose object that transforms a hash into calls on methods of some given object
class DelegateHash
def initialize(target, method_hash)
@target = target
@method_hash = method_hash.dup
end
def [](k)
@target.send(@method_hash[k])
end
end
class A
def initialize
@a = DelegateHash.new(self, { 0 => :a })
end
def a()
puts "hello world"
end
def b()
@a[0]
end
end
x = A.new
x.a #=> prints "hello world"
x.b #=> prints "hello world"
One other basic error that you made is that you initialized @a
outside of any instance method -
just bare inside of the definition of A
. This is a big time no-no, because it just doesn't work.
Remember, in ruby, everything is an object, including classes, and the @
prefix means the instance
variable of whatever object is currently self. Inside an instance method definitions, self
is an instance
of the class. But outside of that, just inside the class definition, self
is the class object - so you defined
an instance variable named @a
for the class object A
, which none of the instances of A
can get to directly.
Ruby does have a reason for this behaviour (class instance variables can be really handy if you know what
you're doing), but this is a more advanced technique.
In short, only initialize instance variables in the initialize
method.
回答5:
Well, the first line in your class calls a method that doesn't exist yet. It won't even exist after the whole class is loaded though, since that would be a call to the class method and you've only defined instance methods.
Also keep in mind that {0 => a()} will call the method a(), not create a reference to the method a(). If you wanted to put a function in there that doesn't get evaluated until later, you'd have to use some kind of Lambda.
回答6:
I am pretty new to using callbacks in Ruby and this is how I explained it to myself using an example:
require 'logger'
log = Logger.new('/var/tmp/log.out')
def callit(severity, msg, myproc)
myproc.call(sev, msg)
end
lookup_severity = {}
lookup_severity['info'] = Proc.new { |x| log.info(x) }
lookup_severity['debug'] = Proc.new { |x| log.debug(x) }
logit = Proc.new { |x,y| lookup_sev[x].call(y) }
callit('info', "check4", logit)
callit('debug', "check5", logit)
回答7:
a = ->(string="No string passed") do
puts string
end
some_hash = { 0 => a }
some_hash[0].call("Hello World")
some_hash[0][]