2016-12-28 2 views
2

In diesem Beispiellokale Variablen in den Blöcken

x = 1 

foo = lambda do 
    x = 2 
end 

foo.call 

foo den Wert von x ändern. Ist es möglich, den Wert von x zu verbergen, so dass es vor und nach dem Aufruf von proc gleich bleibt?

+1

Haben Sie wirklich vor dem Posten gesucht?[Hier gehts] (http://stackoverflow.com/questions/16699595/how-to-use-truly-local-variables-in-ruby-proc-lambda) – 31piy

+0

Ja, jedoch gibt es keine Antwort auf meine Frage. Ich kann nutzen ; um die Variable als lokal zu deklarieren, aber dann ist der Anfangswert nicht im Lambda sichtbar. Ich suchte nach etwas wie C++ - Blockverhalten. – Surfrider

+1

Sie können 'x' im Lambda übergeben, wenn Sie Lambda als' foo = Lambda | y | deklarieren mach es. Auf diese Weise wird "y" nicht mit der äußeren Variablen "x" in Konflikt geraten. – 31piy

Antwort

6

Block-lokale Variablen

Ich bin damit einverstanden, es ist nicht sehr googleable, wenn Sie nicht den genauen Begriff kennen. Hier gehen Sie:

x = 1 

foo = lambda do 
    x = 2 
end 

foo.call 

p x # => 2 

x = 1 

foo = lambda do |;x| 
    x = 2 
end 

foo.call 

p x # => 1 

Es ist seit Ruby 1.9 zur Verfügung, aber ich komme nicht über sie oft (wenn überhaupt). Es ist here beschrieben, und es ist "block lokale Variable" oder "Block-local Argument" genannt:

Sie können auch block lokale Argumente zu einem Block deklarieren mit ';' in der Blockargumentliste. Zuordnen zu einem Block-local Argument nicht überschreiben lokale Argumente außerhalb des Blocks in den Anwendungsbereich des Anrufers

Alternative

@EliSadoff und @tadman einen sehr wichtigen Punkt in den Kommentaren machen.

Es gibt einen guten Grund, warum diese lokalen Blockvariablen nicht häufig verwendet werden: Es ist normalerweise keine gute Idee, eine äußere Variable in einem Block zu schattieren. Es könnte den Code schwerer lesbar machen, schwerer zu verstehen und schwieriger zu verwenden sein.

mit Rubin Blöcken, äußeren und Blockvariablen sind oft verwandt, aber nicht äquivalent:

  • filename als äußere Variable, file als Blockvariable mit IO.open
  • array als äußere Variable, element als Blockvariable mit Array#each
  • strings als äußere Variable, string als Blockvariable mit Array#map

Wenn Sie ein bound variable und x bereits als äußere Variable verwendet wird, verwenden Sie einfach y, i, z, m, n oder whatever.

+1

Nachdem das gesagt wurde, wird es im Allgemeinen als guter Stil angesehen, äußere Variablen in Blöcken nicht zu schattieren, selbst wenn es eine block-lokale Variable ist. –

+1

Die '|; x |' Methode ist eine Lösung dafür, aber es ist sehr unregelmäßig und am besten zu vermeiden. Namen zu überschatten ist ein Problem, das vermieden werden sollte, nicht mit Tricks überflutet werden. Das ist wirklich als letztes Mittel für Situationen gedacht, in denen es keine andere Möglichkeit gibt. – tadman

+1

Wow, wow, Ruby überrascht mich nie mit unbekannten Features. – akuhn

3

Blöcke sind keine Verschlüsse.

Um das Verhalten zu erreichen Sie suchen (einen Zugriff auf die äußerste Variable bekommen und es in wechselnden frei sein) könnte man wie folgt vorgehen:

x = 1 
foo = ->(y = x) { puts y; y = 2; puts y } 
foo.() 
puts x 
#⇒ 1 
# 2 
# 1 

Es ist unklar, warum man nicht die verwenden können, Derselbe Name für die lokale Blockvariable.

foo = ->(x = x) { puts x; x = 2; puts x } 
#⇒  # nil ?! WHY? 
# 2 

Ich würde die letztere als einen Fehler im Ruby-Interpreter betrachten.

+0

Interessant, wie immer. Zumindest ist es konsistent mit dem anderen Feature/bug 'a = a # => nil, wenn 'a' undefiniert ist. Der Interpreter ist jedoch nett genug, um "Warnung: Zirkularargument-Referenz - x" zu sagen. –

+0

@EricDuminil "Der Interpreter ist nett genug zu sagen 'Warnung: Zirkel Argument Referenz - X'" - beginnend mit 2.2 und wegen der meisten nicht verwandten [brechende Änderung] (https://bugs.ruby-lang.org/issues/10314) :) – mudasobwa