2013-08-28 8 views
9

JavaScript glückliche Zeiten Spaß LandGibt es eine Ruby-Entsprechung für JavaScript Function.prototype.bind?

// make a method 
var happy = function(a, b, c) { 
    console.log(a, b, c); 
}; 

// store method to variable 
var b = happy; 

// bind a context and some arguments 
b.bind(happy, 1, 2, 3); 

// call the method without additional arguments 
b(); 

Ausgang. Yay!

1 2 3 

In Ruby

# make a method 
def sad a, b, c 
    puts a, b, c 
end 

# store method to variable 
b = method(:sad) 

# i need some way to bind args now 
# (this line is an example of what i need) 
b.bind(1, 2, 3) 

# call the method without passing additional args 
b.call 

Wunsch Ausgang

1, 2, 3 

Für das, was es wert ist, ich weiß JavaScript den Kontext der Bindung mit dem ersten Argument ändern übergeben an .bind. In Ruby wäre ich zu 99% glücklich, auch wenn ich den Kontext nicht ändern könnte. Ich muss in erster Linie einfach Parameter an die Methode binden.

Frage

Gibt es eine Möglichkeit Parameter zu einer Instanz einer Ruby zu binden Method so dass, wenn ich method.call ohne zusätzliche Parameter aufrufen, werden die gebundenen Parameter noch an die Methode übergeben?

Tor

Dies ist ein gemeinsames Idiom JavaScript und ich denke, es ist in jeder Sprache nützlich sein würde. Das Ziel besteht darin, eine Methode M an einen Empfänger R zu übergeben, wobei R keine intrinsische Kenntnis davon benötigt (oder hat), welche (oder wie viele) Parameter an M gesendet werden müssen, wenn R die Methode ausführt.

Ein JavaScript-Demonstration, wie dies könnte

/* this is our receiver "R" */ 
var idiot = function(fn) { 
    console.log("yes, master;", fn()); 
}; 


/* here's a couple method "M" examples */ 
var calculateSomethingDifficult = function(a, b) { 
    return "the sum is " + (a + b); 
}; 

var applyJam = function() { 
    return "adding jam to " + this.name; 
}; 

var Item = function Item(name) { 
    this.name = name; 
}; 


/* here's how we might use it */ 
idiot(calculateSomethingDifficult.bind(null, 1, 1)); 
// => yes master; the sum is 2 

idiot(applyJam.bind(new Item("toast"))); 
// => yes master; adding jam to toast 
+0

Ihre Frage Frau bitte? :) –

+1

Ich bin kaum eine Referenz, aber ich habe Ruby nie so geschrieben gesehen. Ich bin neugierig ... Gibt es einen besonderen Grund für diesen Ansatz? Was versuchst du zu erreichen? – Mohamad

+0

@Mohamad Dies ist ein gängiges JavaScript-Idiom. Ich habe der Frage einige Informationen hinzugefügt. – naomik

Antwort

6

Normalerweise Rebinding Methoden nützlich sein ist nicht etwas, das man in Ruby zu tun. Stattdessen verwenden Sie Blöcke:

# This is our receiver "R" 
def idiot(&block) 
    puts("yes, master; #{block.call}") 
end 


# Here's a couple method "M" examples 
def calculateSomethingDifficult(a, b) 
    return "the sum is #{a + b}" 
end 

def applyJam(object) 
    return "adding jam to " + object.name 
end 

class Item 
    attr_reader :name 
    def initialize(name) 
    @name = name 
    end 
end 


# Here's how we might use it 
idiot do 
    calculateSomethingDifficult(1, 1) 
end 
#=> yes master; the sum is 2 

# You *can* change calling context too (see instance_exec), but I'd 
# discourage it. It's probably better to just pass the object as a 
# parameter. 
idiot do 
    applyJam(Item.new("toast")) 
end 
#=> yes master; adding jam to toast 

Wenn Sie wirklich zu „binden“ Methoden wollen, wie Sie in JavaScript zu tun ist es auf jeden Fall möglich, aber:

class Method 
    def bind *args 
    Proc.new do |*more| 
     self.call *(args + more) 
    end 
    end 
end 

Das sollte Ihr Beispiel Arbeit machen fast wie ursprünglich beschrieben:

# make a method 
def sad a, b, c 
    puts a, b, c 
end 

# store method to variable 
b = method(:sad) 

# Get a "bound" version of the method 
b = b.bind(1, 2, 3) 

# call the method without passing additional args 
b.call 

Wenn Sie Brauchen Sie es genau, können Sie wahrscheinlich definieren Object#bindable_method einige BindableMethod Klasse zurückgeben, die das tut, was Sie wollen. In den meisten Fällen denke ich, dass das obige für Sie funktionieren sollte.

+0

Dies würde die Methode zum Zeitpunkt der Bindung ausführen. Der Punkt besteht darin, Parameter an die Methode zu binden, aber den tatsächlichen Aufruf der gebundenen Methode bis zu einem späteren Zeitpunkt zu verschieben. – naomik

+0

@naomik Nein, wird es nicht; Versuche es! ;) Es gibt allerdings eine Einschränkung, die ich vergessen habe zu erwähnen. Eine Sekunde, während ich meine Antwort bearbeite. – Ajedi32

+0

Oh, du hast Recht. Sie wickeln die ursprüngliche Methode mit einem neuen Proc. Ich sehe jetzt. Hmm ... geben Sie mir einen Moment, um die Auswirkungen hier zu betrachten ... – naomik

4

Proc#curry in Ruby ähnelt bind in JavaScript.

def happy(a, b, c, d = 100) 
    puts a, b, c, d 
end 

proc = method(:happy).to_proc.curry # proc is now a curried Proc 

b = proc.call(1,2) # b is a curried Proc with 1 and 2 bound as the first arguments 

b.call(3) # Call the proc, providing the 3rd argument 

Sie können nicht genau duplizieren Sie Ihre Beispielcode, weil, wenn die curried proc mit den notwendigen Argumenten aufgerufen wird, ist es das Ergebnis der proc zurückgibt --- in anderen Worten, man kann nicht alle Argumente binden und Dann rufe den Proc später auf - du musst mindestens ein Argument nicht gebunden lassen.

Dies ist nicht unbedingt eine bessere Option als der Code von Ajedi32, aber ich denke, es ist erwähnenswert, weil es in Ruby eingebaut ist.

Siehe die Dokumentation hier: http://ruby-doc.org/core-2.2.0/Proc.html#method-i-curry

+0

Vielen Dank für das Teilen – naomik

Verwandte Themen