Anmelden

Archiv verlassen und diese Seite im Standarddesign anzeigen : Gibt es lokale "Script" Variablen?



Cornix
22.01.2010, 13:23
Meine heutige Frage:
Kann ich innerhalb einer Klasse eine lokale Variable auf eine Methode verweisen lassen?
Als Beispiel etwas in der Art:



class Example_Class

attr_accessor :method

def initialize(method)
@method = method
self.@method
end

def example_method
self.do_stuff
end

end
wobei die Variable @method dann 'example_method' speichern würde und ich damit dann exakt diese Methode aufrufen könnte.

Ich weis, dass diese Methode nicht funktioniert, worum es mir hiermit vielmehr geht ist ob eine Vorgehensweise nach diesem Schema in irgendeinerweise möglich wäre.

Falls es dies nicht ist, würde es mich auch sehr freuen falls ihr mir dies mitteilen könntet anstatt diese Frage unbeantwortet zu lassen da ihr euch nicht hundert prozentig sicher seid.

Ich danke schoneinmal für jede Antwort.
Cornix.

The_Burrito
22.01.2010, 15:07
Das geht in der Tat. Dafür brauchst du nur ein Objekt der Klasse Method oder UnboundMethod
Solche Objekt erhältst du indem du die Methode method eines beliebigen Objektes aufrufst oder instance_method für Klassen und Module.

Mit der Methode call eines solchen Objektes kannst du die entsprechende Methode dann aufrufen.

Beispiel:


m = 12.method(:to_s)
m.call # => "12"

m = 12.method("+")
m.call(12) # => 24

m = "abc".method(:reverse)
m.call # => "cba"

Ein Method Objekt merkt sich dabei immer den Empfänger von dem es ermittelt wurde.
Hast du eine Instanz von UnboundMethod musst du dieses erst mit der bind Methode einer Instanz von einem Objekt zuweisen.

Cornix
23.01.2010, 18:23
Tut mir leid aber ich scheine wohl beim Versuch das zu verstehen an meine Grenzen zu stoßen.

Also, du sagst ich brauche eine neue Klasse "Methode" ist das korrekt?
Und diese ist nicht vorgefertigt oder ist sie es doch?

und über folgenden Befehl:
m = example_method.method(:to_s)
könnte ich die Methode 'example_method' in der lokalen Variable m speichern und über m.call abrufen?
Wobei 'm' dann eine Instanz dieser neuen Methoden Klasse wäre?

Falls dies falsch ist bitte ich um Verbesserung, es ist mir noch einiges unklar. Vielen Dank aber schoneinmal für diesen Beitrag.
Cornix

The_Burrito
23.01.2010, 22:28
Nein. Die Klasse heißt Method und nicht Methode (also Englisch, und nicht Deutsch).
Eine Instanz von dieser Klasse bekommst du wenn du von einem beliebigen Objekt, die methode method aufrufst. Als Parameter erwartet diese Methode den Namen der Methode die du haben möchtest.
Hast du jetzt z.B. eine Instanz vom Typ Numeric kannst du dir von dieser Instanz aus quasi eine Referenz auf jede ihrer Methoden holen.



Beispiel:

i = 12 # Weist der Variable i eine Instanz vom Typ Numeric zu
m = i.method(:to_s) # m ist jetzt eine Instanz vom Typ Method welche auf die Methode to_s von i verweist
m.call # Ruft die Methode to_s auf, welche an i gebunden ist
i.to_s # Gleichwertig mit dem oberen Statement

Cornix
24.01.2010, 21:42
Okay, nun verstehe ich soweit, vielen Dank.
Allerdings muss ich sagen, dass mein erster Versuch damit kläglich gescheitert ist.
Hier ein simpler Test-Code:

class Example_Class

attr_accessor :method

def initialize
@m = Example_Class.method(example_method)
@m.call
end

def example_method
print("hi")
end

end
Die Nachricht "hi" wird korrekt angezeigt wenn ich diesen Script starte, allerdings folgt direkt darauf folgende Fehlermeldung:

Script '' line 6: TypeError occured.
nil is not a symbol
Welche ich nicht recht zu verstehen in der Lage bin. Besonders nicht in dem Zusammenhang mit meinem Script.

Vielen Dank für die Hilfe soweit.
Cornix.

The_Burrito
24.01.2010, 22:03
Die method Methode erwartet entweder ein Symbol oder einen String mit dem namen der Methode die du haben willst.
Also entweder :hehe:xample_method (der : ist erforderlich) oder "example_method".

Außerdem kriegst du beim Aufruf der method Methode für eine Klasse keine Instanz von Method sondern von UnboundMethod. Diese musst du erst an einen Empfänger binden bevor du sie verwenden kannst.

Beispiel:


c = Example_Class.new
m = Example_Class.method(:example_method)
m.bind(c)
m.call # Gleichwertig mit c.example_method

Cornix
24.01.2010, 22:30
Tut mir leid, ich scheine wohl einfach kein glückliches Händchen dafür zu haben. Meine Klasse sieht nun wie folgt aus:

class Test

def initialize
@m = Test.method(:example_method)
@m.bind(self)
@m.call
end

def example_method
print("hi")
end

end
Soweit nichts sonderlich kompliziertes (ich hoffe ich darf die Methode @m an die eigene Klasse binden) allerdings erhalte ich nun bei jedem Versuch folgenden Fehler:


Script '' line 4: NameError occured
undefinde Method 'example_method' for class 'Class'
Diese Fehlernachricht ergibt soweit Sinn, allerdings nicht im Kontext.
Einmal wieder bitte ich um Aufklärung, vielen Dank für die ganze Mühe und Zeit.

The_Burrito
25.01.2010, 06:43
Wenn du schon im vorhinein den Empfänger kennst (in deinem Fall also self) wäre es sinnvoller, die Methode direkt vom Emfänger abzufragen, also self.method(:example_method). Dann kannst du dir den bind aufruf sparen.

Der Code wie du ihn geschrieben hast, funktioniert wenn du Example_Class.instance_method aufrufst. Hab ich in meinem letzten Post übersehen.

Das führt zu zwei möglichkeiten:



class Test

def initialize
@m = Test.instance_method(:example_method)
@m = @m.bind(self) # bind liefert nur eine Method instanz die an self gebunden ist zurück, bindet es aber nicht an @m
@m.call
end

def example_method
print("hi")
end

end




class Test

def initialize
@m = self.method(:example_method) # Liefert ein Method Objekt, welches direkt an self gebunden ist
@m.call
end

def example_method
print("hi")
end

end

Cornix
25.01.2010, 11:41
Jetzt funktioniert es, vielen vielen Dank.
Dies hier war was ich übersehen habe:

@m = @m.bind(self)
habe ich mit

@m.bind(self)
verwechselt.

Es brauchte eine lange Zeit aber nun funktioniert es, vielen Dank.

-KD-
27.01.2010, 17:45
Method-Objekte zu nutzen hat allerdings auch 'ne Menge Nachteile. Zum einen bekommst du wirklich nur Zugriff auf Methoden, nicht aber auf andere mögliche Responses von Objekten, weiterhin ist die Performance auch nicht so gut, weil die komplette Methode dabei im Speicher kopiert wird.

Der gängige Weg in Ruby um eine Methode mit einem variablen Namen und variablen Parametern auszuführen ist Object#send. Das bekommt als ersten Parameter den Namen der Methode (entweder als String oder Symbol) und zusätzlich noch die Parameter die man der Methode mitschicken will.


5.send(:+, 3) #=> 8
4.send("*", 8) #=> 32
a = [1, nil, 3, nil]
a.send("compact!") #=> [1, 3]
a.send(:push, 4, 5, 6) #=> [1, 3, 4, 5, 6]

Cornix
28.01.2010, 13:12
Das ist natürlich eine sehr sehr nützliche Information, vielen Dank.