2 Stimmen

Wie rufe ich eine Methode auf, die ein Hash-Wert ist?

Zuvor hatte ich nach einer cleveren Möglichkeit gefragt, eine Methode unter einer bestimmten Bedingung auszuführen " Ruby - ein cleverer Weg, um eine Funktion unter einer Bedingung auszuführen ."

Die Lösungen und die Reaktionszeit war großartig, obwohl, nach der Implementierung, mit einem Hash von Lambdas wird ziemlich schnell hässlich. Also begann ich zu experimentieren.

Der folgende Code funktioniert:

def a()
  puts "hello world"
end

some_hash = { 0 => a() }

some_hash[0]

Aber wenn ich dies in eine Klasse verpacke, 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 sehe nicht, warum es nicht mehr funktionieren sollte. Kann jemand einen Vorschlag machen, wie man es wieder zum Laufen bringt?

7voto

Ben Hughes Punkte 13721

Dieser code funktioniert nicht. er führt aus a zu dem Zeitpunkt, zu dem es dem Hash hinzugefügt wird, und nicht, wenn es aus dem Hash abgerufen wird (versuchen Sie es in irb).

Es funktioniert nicht in der Klasse, weil es keine a Methode, die in der Klasse definiert ist (Sie definieren eventuell eine Methode a auf der Instanz.

Versuchen Sie, Lambdas zu verwenden wie

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

stattdessen

0 Stimmen

Das wollte ich eigentlich vermeiden. Der Code, den ich in das Lambda einfügen würde, kann ein bisschen unübersichtlich werden, und ich werde etwa 15 Elemente im Hash haben. Das macht den Code ein bisschen unübersichtlich.

0 Stimmen

@Peter: Ist der Code in einem Lambda unordentlicher, als er es in einer Methode wäre?

1 Stimmen

Dann übergeben Sie die Methodennamen als Symbole und verwenden Sie send oder Symbol#to_proc.

5voto

Chuck Punkte 228137

Zunächst einmal setzen Sie kein Lambda in den Hash ein. Sie fügen das Ergebnis des Aufrufs von a() im aktuellen Kontext.

Überlegen Sie anhand dieser Informationen, was der Code in Ihrer Klasse bedeutet. Der Kontext einer Klassendefinition ist die Klasse. Sie definieren also eine Instanzmethode namens a und weisen dem Hash eine Klasseninstanzvariable zu, die das Ergebnis des Aufrufs a im aktuellen Kontext. Der aktuelle Kontext ist die Klasse A, und die Klasse A verfügt nicht über eine Klassenmethode namens a Sie versuchen also, dort das Ergebnis einer nicht existierenden Methode zu platzieren. Dann in der Instanzmethode b versuchen Sie, auf eine Instanzvariable namens @a -- aber es gibt keine. Die @a die im Klassenkontext definiert ist, gehört zu der Klasse selbst, nicht zu einer bestimmten Instanz.

Wenn Sie also ein Lambda wollen, müssen Sie erstens ein Lambda erstellen. 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

Dadurch wird der Bedingungs-Hash als Methode der Klasse definiert (was den Zugriff erleichtert), und der Hash enthält lediglich den Namen der aufzurufenden Methode und kein Lambda oder ähnliches. Wenn Sie also aufrufen b(0) es send s selbst die in A.conditions[0] enthaltene Nachricht, die lautet a .

3voto

rampion Punkte 84270

Wenn Sie diese Art von Dingen wirklich nur verschönern wollen, warum verpacken Sie nicht alle Ihre Methoden in eine Klasse wie diese:

# 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 von @a außerhalb einer Instanzmethode - nur innerhalb der Definition von A . Das ist ein absolutes No-Go, weil es einfach nicht funktioniert. Denken Sie daran, dass in Ruby alles ein Objekt ist, einschließlich Klassen, und die @ Präfix bedeutet die Instanz Variable des Objekts, das gerade self ist. Innerhalb einer Instanzmethode Definitionen, self ist eine Instanz der Klasse. Aber außerhalb davon, nur innerhalb der Klassendefinition, self ist das Klassenobjekt - Sie haben also eine Instanzvariable namens @a für das Klassenobjekt A , die keine der Instanzen von A direkt erreicht werden kann.

Ruby hat einen Grund für dieses Verhalten (Klasseninstanzvariablen können sehr nützlich sein, wenn man weiß, was wenn man weiß, was man tut), aber dies ist eine fortgeschrittene Technik.

Kurz gesagt, initialisieren Sie nur Instanzvariablen in der initialize Methode.

3voto

wiki Punkte 31
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 )

1voto

Phil Kulak Punkte 6292

Nun, die erste Zeile in Ihrer Klasse ruft eine Methode auf, die noch nicht existiert. Sie wird auch nicht existieren, nachdem die ganze Klasse geladen ist, da dies ein Aufruf der Klassenmethode wäre und Sie nur Instanzmethoden definiert haben.

Denken Sie auch daran, dass {0 => a()} die Methode a() aufruft und nicht einen Verweis auf die Methode a() erzeugt. Wenn Sie dort eine Funktion einfügen wollten, die erst später ausgewertet wird, müssten Sie eine Art Lambda verwenden.

0 Stimmen

Ah, das habe ich nicht gewusst. Gibt es eine Möglichkeit, das zu erreichen, was ich vorhatte?

0 Stimmen

Sie könnten etwa so vorgehen: @a = {0 => lambda{A.new.a()}} Dann müssten Sie es mit @a[0].call aufrufen

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X