Question Quelle magie dans «notre» ou «utiliser vars» satisfait à «utiliser strict qw (vars)»?


J'ai du code de travail, mais j'essaie de comprendre Pourquoi Ça marche. J'essaie aussi d'en apprendre plus sur les internes de Perl 5 (perlbrew, perl-5.26.1, Cygwin x64).

Je sais de perlvar et strict cette use strict 'vars' fonctionne en mettant des drapeaux dans $^H. Perl teste ensuite les accès aux non-:: variables basées sur ces drapeaux. D'une certaine manière, les deux our et use vars marquer les variables pour qu'elles réussissent le test. Comment font-ils cela?

Par exemple:

perl -E 'package Foo;
    use strict "vars";
    use vars qw($foo);
    say $foo;'

fonctionne bien (bien qu'il ne produise aucune sortie). Basé sur la source pour use vars, J'ai essayé ceci, qui selon moi aurait le même effet:

perl -E 'package Foo;
    use strict "vars";
    my $sym = "Foo::foo";          # <-- These two lines pulled straight
    *$sym = \$$sym;                # <-- from the source for the vars pragma
    say $foo;'

Cependant, cela m'a donné une erreur: Global symbol "$foo" requires explicit package name. J'ai aussi essayé $sym = "::Foo:foo" dans ce qui précède, avec le même résultat.

J'ai vérifié, et $Foo::foo est dans la table des symboles:

$ perl -E 'package Foo;
      use Data::Dumper;
      use strict "vars"; 
      my $sym = "Foo::foo";
      *$sym = \$$sym; 
      say Dumper(\%{"Foo::"});'    # <-- Foo's symbol table

Output: 
$VAR1 = {
    'BEGIN'  => *Foo::BEGIN,
    'Dumper' => *Foo::Dumper,
    'foo'    => *Foo::foo          # <-- yep, it's there
};

Quoi d'autre est use vars faire ça me manque? Est-ce que our faire la même chose ou quelque chose de différent?

Mettre à jour

Voici un A / B basé sur réponse de melpomene:

Fails                         Succeeds
-------------------------     ----------------------------------
package Foo;                  package Foo;
use strict "vars";            use strict "vars";

                              BEGIN {
                                  package Bar;
my $sym="Foo::foo";               my $sym = "Foo::foo";
*$sym = \$$sym;                   *$sym = \$$sym;
                              }
say $foo;                     say $foo;

10
2018-04-09 17:48


origine


Réponses:


use strict 'vars' fonctionne en mettant des drapeaux dans $^H.

Oui, mais c'est un détail d'implémentation. $^H expose des bits d'état d'interpréteur interne, mais vous n'êtes pas censé le toucher en code normal.

D'une certaine manière, les deux our et use vars marquer les variables pour qu'elles passent le test. Comment font-ils cela?

Ceci est également considéré comme un détail d'implémentation.


Cependant, nous pouvons jeter un coup d'oeil sous le capot. strict "vars" se plaint de variables non déclarées (au moment de la compilation).

Il existe une liste codée de variables exemptes de cette vérification. il comprend toutes les variables de ponctuation (par ex. $/, $_, etc. $a et $b (utilisé par sort)).

Toutes les variables déclarées lexicalement (c'est-à-dire localement) passent également strict; c'est ainsi my, our, et state travail. (Pour nos fins local n'est pas une déclaration et ne crée pas de variables locales; local modifie temporairement la valeur d'une variable existante.)

La troisième exception concerne les variables exportées à partir des modules. L'utilisation de variables globales dans le cadre de l'interface de votre module est généralement considérée comme une mauvaise idée, mais certains modules plus anciens le font toujours. English exporte également des variables parce que c'est tout son but, nous allons donc l'utiliser comme exemple:

use strict;
use English qw($INPUT_RECORD_SEPARATOR);
$INPUT_RECORD_SEPARATOR = "";  # <--
my $paragraph = readline STDIN;

La ligne marquée <-- ne génère pas d'erreur car Perl se souvient des variables importées d'un module.

Que signifie "exporter"? Cela signifie simplement aliaser un symbole au-delà des limites du package:

*main::foo = \$Some::Module::foo;  # now $main::foo is an alias for $Some::Module::foo

Ce qui est curieux, c'est qu'en ce qui concerne les composants internes de Perl, une variable est "importée" si elle a été associée à un alias dans un autre package. Peu importe ce qui a été aliasé à; tout ce qui compte, c'est où l'alias s'est produit. use vars (ab-) utilise ce détail pour contourner strict "vars" en exportant vos propres variables vers vous:

package Some::Package;
use vars qw($foo);

fonctionne comme

package Some::Package;
BEGIN {
    package vars;
    *Some::Package::foo = \$Some::Package::foo;
}
# now $foo is an alias to ... itself

L’autre morceau du puzzle est que use arrive au moment de la compilation, comme BEGIN des blocs. Votre exemple échoue car votre tentative d'alias ne se produit qu'à l'exécution, ce qui est trop tard pour strict, et parce qu'il ne bascule pas vers un autre package pour effectuer l'aliasing.


À la fin vars est juste un module, écrit en clair Perl. our est différent: c'est un vrai mot-clé et une partie du langage. Il a également un comportement différent: il crée effectivement un alias (à une variable de package), mais cet alias se trouve dans une étendue locale et non dans la table des symboles.

Considérer par exemple le suivant:

my $foo = 2;
{
    our $foo = "hello";
    print "foo = $foo; main::foo = $main::foo\n";
}
print "foo = $foo; main::foo = $main::foo\n";

Cette sorties

foo = hello; main::foo = hello
foo = 2; main::foo = hello

parce que l'intérieur our $foo déclaration obscurcit l'extérieur $foo dans le bloc intérieur. Dans le bloc à la fois $foo et $main::foo se référer à la même variable; à l'extérieur $foo se réfère au lexical my $foo, qui est intact.

Une autre différence à use vars:

use strict;
package Foo;
our $x = "hello";
package Bar;
print "$x\n";  # hello

Ce code fonctionne bien car package les déclarations ne créent pas de nouvelle portée. Il n'y a qu'une seule unité de cadrage ici (le fichier entier), et ainsi our $x fait du $x faire référence à $Foo::x pour le reste du fichier, quel que soit le package dans lequel vous basculez.

D'autre part:

use strict;
package Foo;
use vars qw($x);
$x = "hello";
package Bar;
print "$x\n";

Ce code ne compile même pas. La référence à $x dans la dernière ligne ne peut pas être résolu: Perl vérifie d'abord la portée locale, mais il n'y a pas de déclaration locale $x's. Il vérifie ensuite le package en cours (Bar) et ne trouve rien non plus, et sans strict "vars" il aurait automatiquement créé $Bar::x pour vous, mais avec strict "vars" activé c'est simplement une erreur. $Foo::x est sans importance et jamais vérifié.


15
2018-04-09 18:34