2011-01-12 12 views
17

Eines der ersten Dinge, die ich versuche, in einer unbekannten Programmiersprache zu lernen, ist, wie es Verschlüsse handhabt. Ihre Semantik ist oft damit verwoben, wie die Sprache Bereiche und verschiedene andere knifflige Bits behandelt, so dass sie mehrere andere Aspekte der Sprache erkennen lassen. Plus, Schließungen sind ein sehr mächtiges Konstrukt und oft auf die Menge an Boilerplate, die ich tippen muss. So war ich mit Perl Schließungen rumgespielt und ich stolperte über eine kleine Gotcha:

my @closures; 
foreach (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 

Wenn ich den obigen Code schrieb ich erwartete

I will remember 1 
I will remember 2 
I will remember 3 

zu sehen, aber stattdessen habe ich I will remember CODE(0x986c1f0).

Das obige Experiment ergab, dass $_ sehr kontextabhängig ist und wenn es in einer Schließung erscheint, dann ist sein Wert nicht an dem Punkt der Erstellung der Schließung festgelegt. Es verhält sich eher wie eine Referenz. Welche anderen Probleme sollte ich beim Erstellen von Schließungen in Perl beachten?

Antwort

25

Verschlüsse schließen nur über lexikalische Variablen; $_ ist normalerweise eine globale Variable. In 5.10 und darüber können Sie my $_; sagen, dass es in einem bestimmten Bereich lexikalisch sein soll (obwohl in 5.18 dies rückwirkend als experimentell deklariert wurde und sich ändern kann, also besser, einen anderen Variablennamen zu verwenden).

Dies erzeugt die Ausgabe, die Sie erwarten:

use strict; 
use warnings; 
use 5.010; 
my @closures; 
foreach my $_ (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 
+2

Aber das ist so ziemlich das gleiche wie die Verwendung von 'foreach my $ num ...'. – davidk01

+1

Also, wenn sie nur über lexikalische Variablen schließen, was passiert mit anderen Arten von Variablen? Verwendet der Abschluss den Wert, der während der Aufrufseite verfügbar ist, wie für '$ _'? – davidk01

+1

@ davidk01: Wie in jeder anderen Programmiersprache, die globale Variablen erlaubt, schließen sich Schließungen über diese Variable wie normal. Sie haben nur vergessen, dass es global ist und sich daher ändern kann, wenn anderer Code seinen Wert auf etwas anderes setzt (wie beim anonymen Sub selbst, wenn es aufgerufen wird). – slebetman

3

$ _ ist eine globale Variable und nicht in Schließung verwendet werden soll. Bevor Sie es verwenden, weisen Sie es einer lexikalisch begrenzten Variablen zu, wie unten gezeigt. Dies wird erwartete o/p produzieren.

#!/usr/bin/perl -w 

use strict; 
my @closures; 


foreach (1..3) { 
    my $var = $_; 
    push @closures, sub { print "I will remember $var"; }; 
} 

foreach (@closures) { 
    $_->(); 
    print "\n"; 
}