2009-07-01 7 views
2

Auparavant, j'ai demandé une façon intelligente d'exécuter une méthode sur une condition donnée "Ruby a clever way to execute a function on a condition". Les solutions et le temps de réponse étaient excellents, bien que, lors de la mise en œuvre, avoir un hachage de lambdas devient moche assez rapidement. J'ai donc commencé à expérimenter.Comment appeler une méthode qui est une valeur de hachage?

Le code suivant fonctionne:

def a() 
    puts "hello world" 
end 

some_hash = { 0 => a() } 

some_hash[0] 

Mais si je conclurai cela dans une classe, il cesse de fonctionner:

class A 

    @a = { 0 => a()} 

    def a() 
    puts "hello world" 
    end 


    def b() 
    @a[0] 
    end 

end 

d = A.new() 

d.b() 

je ne vois pas pourquoi il devrait cesser de travailler, peut-on suggérer comment pour le faire fonctionner?

Répondre

8

ce code ne fonctionne pas. il exécute a au moment où il est ajouté au hachage, pas quand il est extrait du hachage (essayez-le dans irb).

Il ne fonctionne pas dans la classe parce qu'il n'y a pas de méthode définie a la classe (on finit par définir une méthode a sur l'instance.

Essayez en fait en utilisant lambdas comme

{0 => lambda { puts "hello world" }} 

place Tout d'abord, vous ne mettez pas un lambda dans le hash, vous mettez le résultat de l'appel a() dans le contexte actuel

+0

qui est vraiment ce que je essayait d'éviter. Le code que je mettrais dans le lambda peut être un peu brouillon, et j'aurai environ 15 éléments dans le hachage. Cela encombre un peu le code. – Peter

+0

@Peter: Le code est-il plus lâche dans un lambda que dans une méthode? – Chuck

+1

puis passez les noms de méthode comme symboles et utilisez send ou Symbol # to_proc. –

1

Eh bien, la première ligne de votre classe appelle une méthode qui n'existe pas encore. Il n'existera même pas après le chargement de la classe entière, car ce serait un appel à la méthode de classe et vous avez seulement défini des méthodes d'instance.

Gardez également à l'esprit que {0 => a()} appellera la méthode a(), sans créer de référence à la méthode a(). Si vous vouliez y mettre une fonction qui ne sera évaluée que plus tard, vous devrez utiliser une sorte de Lambda.

+0

Ah, je ne savais pas que. Eh bien, y a-t-il de toute façon à réaliser ce que j'essayais de faire? – Peter

+0

Vous pouvez faire quelque chose comme ceci: @a = {0 => {lambda A.new.a()}} Ensuite, vous devrez l'appeler avec @a [0] .call –

4

Étant donné cette information, considérez ce que le code dans votre classe signifie. Le contexte d'une définition de classe est la classe. Vous définissez donc une méthode d'instance appelée a et affectez une variable d'instance de classe au hash contenant le résultat de l'appel a dans le contexte actuel. Le contexte actuel est la classe A, et la classe A n'a pas de méthode de classe appelée a, donc vous essayez de mettre le résultat d'une méthode inexistante là. Ensuite, dans la méthode d'instance b, vous essayez d'accéder à une variable d'instance appelée @a - mais il n'y en a pas. Le @a défini dans le contexte de classe appartient à la classe elle-même, pas à une instance particulière. Donc, tout d'abord, si vous voulez un lambda, vous devez faire un lambda. Deuxièmement, vous devez être clair sur la différence entre une classe et une instance de cette classe.

Si vous voulez faire une liste des noms de méthode à appeler certaines conditions, vous pouvez le faire comme ceci:

class A 
    def self.conditions() { 0 => :a } end 

    def a 
    puts "Hello!" 
    end 

    def b(arg) 
    send self.class.conditions[arg] 
    end 
end 

Cela définit le hachage des conditions comme méthode de la classe (ce qui rend facile accéder), et le hachage contient simplement le nom de la méthode à appeler plutôt qu'un lambda ou quelque chose comme ça.Donc, quand vous appelez b(0), il send est lui-même le message contenu dans A.conditions [0], qui est a.

2

Si vous voulez vraiment juste assez ce genre de chose up, pourquoi ne pas envelopper toutes vos méthodes dans une classe comme ceci:

# 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" 

ou, pour utiliser votre exemple:

# 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" 

Une autre erreur de base que vous avez faite est que vous avez initialisé @a en dehors de toute méthode d'instance - juste nue à l'intérieur de la définition de A. C'est un grand moment non-non, parce que ça ne marche pas. Rappelez-vous, dans ruby, tout est un objet, y compris les classes, et le préfixe @ signifie la variable d'instance de tout objet qui est actuellement auto. Dans une définition de méthode d'instance, self est une instance de la classe. Mais en dehors de cela, juste à l'intérieur de la définition de classe, self est l'objet de classe - donc vous avez défini une variable d'instance nommée @a pour l'objet de classe A, auquel aucune des instances de A ne peut accéder directement.

Ruby a une raison de ce comportement (les variables d'instance de classe peuvent être très utiles si vous savez ce que vous faites), mais c'est une technique plus avancée.

En résumé, initialisez uniquement les variables d'instance dans la méthode initialize.

0

a = -> (string = "Aucune chaîne passée") ne

met chaîne

fin

un_hash = {0 => a}

un_hash [0 ] .call ("Bonjour tout le monde")

some_hash [0] []

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) 
1

Je suis assez nouveau à l'utilisation callbacks dans Ruby et voilà comment je me suis expliqué à l'aide d'un exemple:

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) 
Questions connexes