Frage an methods, hash, call, ruby – Wie rufe ich eine Methode auf, die ein Hashwert ist?

2

Zuvor fragte ich nach einer cleveren Möglichkeit, eine Methode unter einer bestimmten Bedingung auszuführen. "Ruby eine clevere Möglichkeit, eine Funktion unter einer Bedingung auszuführen. "

Die Lösungen und die Reaktionszeit waren großartig. Bei der Implementierung wird es jedoch ziemlich schnell hässlich, einen Lambda-Hasch zu haben. Also fing ich an zu experimentieren.

Der folgende Code funktioniert:

def a()
  puts "hello world"
end

some_hash = { 0 => a() }

some_hash[0]

Aber wenn ich das in eine Klasse packe, funktioniert es nicht mehr:

class A

  @a = { 0 => a()}

  def a()
    puts "hello world"
  end


  def b()
    @a[0]
  end

end

d = A.new()

d.b()

Ich kann nicht verstehen, warum es nicht mehr funktionieren soll. Kann jemand vorschlagen, wie es funktioniert?

Deine Antwort

7   die antwort
0

a = -> (string = "Kein String übergeben")

setzt Zeichenfolge

Ende

some_hash = {0 => a}

some_hash [0] .call ("Hallo Welt")

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

sollten Sie all Ihre Methoden in eine Klasse wie die folgende einbinden:

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

oder, um Ihr Beispiel zu verwenden:

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

Ein weiterer grundlegender Fehler, den Sie gemacht haben, ist die Initialisierung@a außerhalb jeder Instanzmethode - nur innerhalb der Definition vonA. Dies ist eine große Zeit, nein, nein, weil es einfach nicht funktioniert. Denken Sie daran, in Ruby ist alles ein Objekt, einschließlich Klassen, und die@ Präfix bedeutet die Instanzvariable des Objekts, das gerade selbst ist. Innerhalb einer Instanz Methodendefinitionen,self ist eine Instanz der Klasse. Aber davon abgesehen, nur innerhalb der Klassendefinition,self ist das Klassenobjekt - so haben Sie eine Instanzvariable mit dem Namen definiert@a für das KlassenobjektA, von denen keine der InstanzenA kann direkt zu bekommen.

Ruby hat einen Grund für dieses Verhalten (Klasseninstanzvariablen können sehr nützlich sein, wenn Sie wissen, was Sie tun), aber dies ist eine fortgeschrittenere Technik.

Kurz gesagt, initialisieren Sie nur Instanzvariablen in derinitialize Methode.

1

die erste Zeile in Ihrer Klasse ruft eine Methode auf, die es noch nicht gibt. Es wird jedoch nicht einmal existieren, nachdem die gesamte Klasse geladen wurde, da dies ein Aufruf der Klassenmethode wäre und Sie nur Instanzmethoden definiert haben.

Beachten Sie auch, dass {0 => a ()} die Methode a () aufruft und keinen Verweis auf die Methode a () erstellt. Wenn Sie eine Funktion einfügen möchten, die erst später ausgewertet wird, müssen Sie eine Art Lambda verwenden.

Ah, das habe ich nicht gemerkt. Gibt es überhaupt etwas zu erreichen, was ich versucht habe? user130076
Sie könnten so etwas tun: @a = {0 => lambda {A.new.a ()}} Dann müssten Sie es mit @a [0] .call aufrufen Phil Kulak
1

und so habe ich es mir selbst anhand eines Beispiels erklärt:

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

ebnis des Aufrufsa() im aktuellen Kontext.

Bedenken Sie anhand dieser Informationen, was der Code in Ihrer Klasse bedeutet. Der Kontext einer Klassendefinition ist die Klasse. Sie definieren also eine Instanzmethode namensa, und weisen Sie dem a-Hash, der das Ergebnis des Aufrufs enthält, eine Klasseninstanzvariable zua im aktuellen Kontext. Der aktuelle Kontext ist die Klasse A, und Klasse A hat keine aufgerufene KlassenmethodeaSie versuchen also, das Ergebnis einer nicht vorhandenen Methode dort abzulegen. Dann in der InstanzmethodebSie versuchen, auf eine Instanzvariable zuzugreifen, die aufgerufen wird@a - aber es gibt keinen. Das@a Die im Klassenkontext definierte Klasse gehört zur Klasse selbst und nicht zu einer bestimmten Instanz.

Wenn Sie also zuallererst ein Lambda wollen, müssen Sie ein Lambda machen. Zweitens müssen Sie sich über den Unterschied zwischen einer Klasse und einer Instanz dieser Klasse im Klaren sein.

Wenn Sie eine Liste von Methodennamen erstellen möchten, die unter bestimmten Bedingungen aufgerufen werden sollen, können Sie dies folgendermaßen tun:

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

  def a
    puts "Hello!"
  end

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

Dies definiert den Bedingungs-Hash als Methode der Klasse (um den Zugriff zu vereinfachen), und der Hash enthält lediglich den Namen der aufzurufenden Methode und nicht ein Lambda oder ähnliches. Also, wenn Sie anrufenb(0)essends selbst die in A.conditions [0] enthaltene Nachricht ista.

7

a Zu dem Zeitpunkt, an dem es zum Hash hinzugefügt wird, nicht, wenn es aus dem Hash abgerufen wird (versuchen Sie es in irb).

Es funktioniert nicht in der Klasse, weil es keine gibta Methode, die für die Klasse definiert wurde (Sie definieren schließlich eine Methodea auf der Instanz.

Versuchen Sie tatsächlich mit Lambdas wie

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

stattdessen

Im Grunde könnten Sie eine Methode wie erstellenself.add_condition(a, &b) in Ihrer Klasse, die einfach den Block b zu einem Hash auf Klassenebene hinzufügt. Anschließend können Sie den Block als Methode abrufen und aufrufen. In deinem Klassenkörper hättest duadd_condition 0 { puts "hello world" } oder so Ben Hughes
@ Peter: Ist der Code in einem Lambda unordentlicher als in einer Methode? Chuck
Ich bin mir nicht sicher, was das überhaupt bedeutet, sorry! Die von Ihnen vorgeschlagene Verwendung von send ist jedoch ideal. user130076
Übergeben Sie dann die Methodennamen als Symbole und verwenden Sie send oder Symbol # to_proc. Ben Hughes

Verwandte Fragen