2012-11-07 8 views
16

Ich sehe gelegentlich begin...end Blöcke in Rubin ohne rescue, else, ensure, etc. Anweisungen dazwischen verwendet. Zum Beispiel:Gibt es unbeabsichtigte Folgen von Rubys "begin ... end" ohne "rettung" als Code-Block?

foo = begin 
    whatever = 3 
    "great" 
    42 
end 

Die Absicht des Coder, so scheint es, ist den begin...end Block nur für seinen Block-Gruppierung Qualität (als ob begin waren do) zu verwenden. Persönlich denke ich, dass diese Art der Nutzung das Prinzip der geringsten Überraschung verletzt (begin bedeutet Ausnahmebehandlung für mich).

Gibt es irgendwelche unbeabsichtigten Folgen der Verwendung von begin...end auf diese Weise? Haben begin...end Blöcke irgendwelche semantischen Unterschiede (vielleicht in der Ausnahmebehandlung?), Die diese Verwendung gefährlich machen?

Rubys Syntax ist unglaublich subtil, und ich wäre nicht überrascht, wenn hier seltsame Fallstricke lauern würden.

+0

Ich habe noch nie einen solchen Code gesehen. Können Sie auf eine öffentliche Quelldatei zeigen? –

+0

@SergioTulentsev: bis ich eine Quelldatei finden kann, hier ist ein Blog-Kommentar: http://blog.rubybestpractices.com/posts/rklemme/003-The_Universe_between_begin_and_end.html # comment-9011441 – pje

+0

Ich weiß nicht, wie es gefährlich wäre, aber IMO ist es falsch: es sollte eine Methode sein. –

Antwort

27

Ich benutze das manchmal, wenn ich etwas einer Variablen zuweisen möchte, aber ich muss den Wert berechnen, den ich zuerst zuweisen möchte. Es macht den Code auf diese Weise ein bisschen aufgeräumter. Ich denke, es ist Benutzervorliebe. Im Grunde sagst du: Ich gebe foo etwas zu, aber um den Wert zu bekommen, den ich will, muss ich zuerst einige Dinge tun. Es ist besonders nützlich, wenn memoization tun, so dass anstelle von

if @cache.nil? 
    do_something! 
    @cache = read_value 
end 

Sie tun können

@cache ||= begin 
    do_something! 
    read_value 
end 

Was Sie nehmen hier den Vorteil, dass die Ruby-Interpreter einen Stapel hat, und jeder Ausdruck wird in der Regel etwas auf Push der Stapel, oder nimm etwas vom Stapel. Die Zuweisung nimmt nur das letzte vom Stapel und ordnet es zu (in diesem Fall die letzte Zeile von Anfang/Ende). Oftmals kann dies nützlich sein (Stack-Ansatz in Ruby).

Ich denke nicht, dass es am wenigsten Überraschung verletzt, aber ich denke, es ist Benutzervorliebe, ob Sie es verwenden wollen oder nicht.

können Sie sehen, dass es nichts tut unerwartet bei der Suche, was Bytecodebefehle es erzeugt in Ruby MRI 1,9:

RubyVM::InstructionSequence::compile("c = begin; a = 5; 6; end").to_a 

[:trace, 1], 
[:trace, 1], 
[:putobject, 5], 
[:setlocal, 2], 
[:trace, 1], 
[:putobject, 6], 
[:dup], 
[:setlocal, 3], 
[:leave] 

Trace nur für Stack-Traces ist, können Sie das ignorieren. Dup dupliziert das letzte Element auf dem Stapel. In diesem Beispiel ist die Nummer der lokalen Variablen a2 und die Nummer der lokalen Variablen c ist 3 (daher wird putobject, 2 der Variablen a zugeordnet usw.). Der einzige Nebeneffekt davon im Vergleich zu a = 5; c = 6 ist die dup Anweisung, was bedeutet, dass die Stackgröße Ihrer Methode um 1 Slot größer ist. Dies ist jedoch nicht besonders wichtig, da es nur Auswirkungen hat, während der Interpreter in dieser speziellen Methode ist und der Speicher für den Stapel sowieso vorreserviert ist. Das bedeutet also nur, dass der Stapelzeiger um 1 mehr dekrementiert wird als sonst. Also grundsätzlich keine Veränderung. Mit optimierten Optimierungen wird wahrscheinlich auch die dup verschwinden.

+1

Das beantwortet meine Frage nicht. – pje

+0

Wie ich schon sagte, es ist absolut gültig, es gibt also keine unbeabsichtigten Konsequenzen. Es ist nur ein Code-Stil. – mrbrdo

+0

und das ist eine gute Meinung zu haben, aber "es gibt keine unbeabsichtigten Folgen" ist nicht in Ihrer Antwort. – pje

Verwandte Themen