76 votes

Où et comment la variable _ (trait de soulignement) est-elle spécifiée?

La plupart sont conscients de l' _s'signification spéciale dans la CISR en tant que titulaire pour la dernière valeur de retour, mais ce n'est pas ce que je veux parler ici.

Au lieu de cela, je veux parler d' _ lorsqu'il est utilisé comme un nom de variable dans la plaine du-vieux-Ruby-code. Ici, il semble avoir un comportement spécial, qui s'apparente à un "ne se soucient pas de la variable" (à la Prolog). Voici quelques exemples illustrant son comportement unique:

lambda { |x, x| 42 }            # SyntaxError: duplicated argument name
lambda { |_, _| 42 }.call(4, 2) # => 42
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_'
lambda { |_| _ + 1 }.call(42)   # => 43
lambda { |_, _| _ }.call(4, 2)  # 1.8.7: => 2
                                # 1.9.3: => 4
_ = 42
_ * 100         # => 4200
_, _ = 4, 2; _  # => 2

Ils ont tous été exécutés dans Ruby directement (avec putss ajouté)-pas de la CISR pour éviter tout conflit avec ses fonctionnalités supplémentaires.

Tout cela est une conséquence de ma propre expérimentation bien, comme je ne trouve aucune documentation sur ce comportement n'importe où (il est vrai que ce n'est pas la chose la plus facile pour la recherche). En fin de compte, je suis curieux de voir comment tout cela fonctionne en interne, afin que je puisse mieux comprendre exactement ce qui est spécial à propos de _. Donc, je suis en demandant des références à la documentation, et, de préférence, le Rubis le code source (et peut-être RubySpec) qui révèlent comment _ se comporte en Ruby.

Remarque: la plupart de cela est née de cette discussion avec @Niklas B.

52voto

mu is too short Points 205090

Il y a quelque chose de la manipulation de la source à supprimer les "doublons de nom d'argument d'erreur". Le message d'erreur apparaît uniquement en shadowing_lvar_gen à l'intérieur d' parse.y, la version 1.9.3 ressemble à ceci:

static ID
shadowing_lvar_gen(struct parser_params *parser, ID name)
{
    if (idUScore == name) return name;
    /* ... */

et idUScore est définie en id.c comme ceci:

REGISTER_SYMID(idUScore, "_");

Vous verrez similaire un traitement spécial en warn_unused_var:

static void
warn_unused_var(struct parser_params *parser, struct local_vars *local)
{
    /* ... */
    for (i = 0; i < cnt; ++i) {
        if (!v[i] || (u[i] & LVAR_USED)) continue;
        if (idUScore == v[i]) continue;
        rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));
    }
}

Vous remarquerez que l'avertissement est supprimé sur la deuxième ligne de l' for boucle.

Le seul traitement spécial des _ que j'ai pu trouver dans le 1.9.3 source est au-dessus: le nom en double erreur est supprimée et le solde non utilisé de la variable d'avertissement est supprimée. D'autres que ces deux choses, _ est juste un simple vieux variable comme les autres. Je ne sais pas du tout de la documentation sur les (petites) caractère spécifique de l' _.

En Ruby 2.0, l' idUScore == v[i] test warn_unused_var est remplacé par un appel à is_private_local_id:

if (is_private_local_id(v[i])) continue;
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));

et is_private_local_id supprime les avertissements pour les variables qui commencent par _:

if (name == idUScore) return 1;
/* ... */
return RSTRING_PTR(s)[0] == '_';

plutôt que de simplement en _ lui-même. Alors 2.0 desserre un peu les choses.

23voto

Matheus Moreira Points 7839

_ est un identificateur valide. Les identificateurs peuvent pas contenir uniquement des caractères de soulignement, ils peuvent aussi être un trait de soulignement.

_ = o = Object.new
_.object_id == o.object_id
# => true

Vous pouvez également l'utiliser comme noms de méthode:

def o._; :_ end
o._
# => :_

Bien sûr, il n'est pas exactement un nom lisible, ni ne transmettra toutes les informations au lecteur sur ce que la variable se réfère à ou de ce que la méthode n'.

IRB, en particulier, définit _ de la valeur de la dernière expression:

$ irb
> 'asd'
# => "asd"
> _
# => "asd"

Comme c'est dans le code source, il suffit simplement de définir _ de la dernière valeur:

@workspace.evaluate self, "_ = IRB.CurrentContext.last_value"

Fait quelques référentiel explorer. Voici ce que j'ai trouvé:

Sur les dernières lignes du fichier id.c, il y a l'appel:

REGISTER_SYMID(idUScore, "_");

greping la source d' idUScore m'a donné deux apparemment des résultats pertinents:

shadowing_lvar_gen semble être le mécanisme par lequel le paramètre formel d'un bloc remplace une variable de même nom existe dans un autre champ. C'est la fonction qui semble soulever "dupliqué argument le nom" SyntaxError et le "ombrage extérieur la variable locale" avertissement.

Après l' greping la source d' shadowing_lvar_gen, j'ai trouvé ce qui suit sur le changelog pour Ruby 1.9.3:

Mar Déc 11 01:21:21 2007 Yukihiro Matsumoto

  • l'analyser.y (shadowing_lvar_gen): non erreur de doublons pour les "_".

Qui est susceptible d'être à l'origine de cette ligne:

if (idUScore == name) return name;

À partir de cela, j'en déduit que, dans une situation telle que proc { |_, _| :x }.call :a, :b, un _ variable simplement les ombres de l'autre.


Voici le commit en question. Essentiellement, il a introduit ces deux lignes:

if (!uscore) uscore = rb_intern("_");
if (uscore == name) return;

À une époque où les idUScore n'existaient même pas, apparemment.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X