2016-06-21 13 views
-2

Ich habe kürzlich herausgefunden, dass die piped Eingänge, die viele Ruby-Methoden verwenden, manchmal nicht notwendig sind, aber das scheint inkonsistent zu sein. Beispielsweise erzeugt diese Zeile ein Array von Strings für gestern, heute und morgen:Wann sind in Ruby Pipeline-Eingaben erforderlich? (| x | x.to_s)

DateTime.now.instance_eval{[prev_day, to_datetime, tomorrow]}.map{|d| d.strftime('%m/%d/%Y')} 
=> ["06/20/2016", "06/21/2016", "06/22/2016"] 

Wie Sie innerhalb instance_eval sehen können, gibt es keine verrohrt Eingang, und die Funktion geht einfach davon aus, dass die Methoden aufgerufen werden DateTime.now. Aber wenn ich versuche, die gleiche Idee auf die map-Methode anzuwenden:

DateTime.now.instance_eval{[prev_day, to_datetime, tomorrow]}.map{strftime('%m/%d/%Y')} 
NoMethodError: undefined method `strftime' for main:Object 

Plötzlich versucht es, die Methode auf Haupt verwenden?

Meine Frage ist, warum funktioniert das in der ersten Methode und nicht in der Sekunde?

Antwort

1

Vom docs for instance_eval:

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj's instance variables and private methods.

instance_eval auf der einen Seite ist etwas ganz Besonderes, weil man es auf ein Beispiel nennt und es wurde explizit baut den Block im Zusammenhang mit Instanz laufen. Daher kann die Methode immer davon ausgehen, dass der Kontext von self immer die Instanz ist, auf die instance_eval aufgerufen wurde.

map auf der anderen Seite ist anders: In Ihrem Beispiel nennen Sie es auf einen Array, aber der Block auf jedes Objekt innerhalb des Arrays laufen soll, daher müssen Sie verschiedene Objekte an den Block in jedem Durchlauf Iteration.

+0

Ah ha! Wenn ich meinen Code durchführe, ist es immer instance_eval, an dem gearbeitet wird. OK, macht jetzt Sinn. – oMiKeY

+0

Das hat eigentlich überhaupt nichts mit 'instance_eval' zu tun. Ein einfacheres Beispiel wäre '[1,2,3] .map {chr}' und '[1,2,3] .map {| x | x.chr} '. Das Ergebnis ist genau dasselbe. –

+0

@Mike: Ich kann dein Beispiel nicht reproduzieren: '[1,2,3] .map {chr}' ergibt 'NameError: undefinierte lokale Variable oder Methode 'chr' für main: Objekt' auf Ruby 2.3.1 – spickermann

0

When are piped inputs necessary in Ruby? (|x| x.to_s)

Piped Eingänge sind erforderlich, wenn ein Block auf jedes Verfahren mit einer yield, die Argumente vergangen ist.

In Ihrem speziellen Fall hat instance_eval die Ausbeute keine Argumente, während map tut. Das Problem, das Sie haben, hat nichts mit instance_eval zu tun. Wenn yield erwartet, dass Sie eine Variable weitergeben, dies jedoch nicht tun, müssen Sie annehmen, dass self das Hauptobjekt ist. Und genau das passiert in Ihrem zweiten Beispiel mit map. Schauen Sie sich dieses Beispiel an.

def yield_to_integer # yield integer 5 to the block 
    yield 5 
end 

yield_to_integer {|int| int.chr} # successfully run .chr() on the Integer 5 
# => "\x05" 
yield_to_integer {chr} # fails to run .chr() because it is not defined on Object 
# NameError: undefined local variable or method `chr' for main:Object