Wo und wie wird die Variable _ (Unterstrich) angegeben?

81

Die meisten sind sich dessen bewusst _ist eine besondere Bedeutung in IRB als Halter für letzten Rückgabewert, aber das ist nicht was ich hier zu fragen.

Stattdessen frage ich nach, _wann sie als Variablenname in normalem Ruby-Code verwendet werden. Hier scheint es ein besonderes Verhalten zu haben, ähnlich einer „egal Variablen“ (à la Prolog ). Hier sind einige nützliche Beispiele, die das einzigartige Verhalten veranschaulichen:

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

Diese wurden alle direkt in Ruby ausgeführt (mit putss hinzugefügt) - nicht IRB -, um Konflikte mit der zusätzlichen Funktionalität zu vermeiden.

Dies ist alles ein Ergebnis meiner eigenen Experimente, da ich nirgendwo eine Dokumentation zu diesem Verhalten finden kann (zugegebenermaßen ist es nicht die einfachste Sache, danach zu suchen). Letztendlich bin ich gespannt, wie das alles intern funktioniert, damit ich besser verstehen kann, woran es besonders ist _. Daher bitte ich um Verweise auf die Dokumentation und vorzugsweise auf den Ruby-Quellcode (und möglicherweise RubySpec ), die zeigen, wie _sich Ruby verhält.

Hinweis: Das meiste davon entstand aus dieser Diskussion mit @Niklas B.

Andrew Marshall
quelle

Antworten:

54

In der Quelle gibt es eine spezielle Behandlung, um den Fehler "Name des doppelten Arguments" zu unterdrücken. Die Fehlermeldung nur in erscheint im shadowing_lvar_genInneren parse.y, die 1.9.3 Version sieht wie folgt aus :

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

und idUScoreist wie folgt definiertid.c :

REGISTER_SYMID(idUScore, "_");

Sie werden ähnliche spezielle Handhabung in sehen 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]));
    }
}

Sie werden feststellen, dass die Warnung in der zweiten Zeile der forSchleife unterdrückt wird.

Die einzige spezielle Behandlung _, die ich in der Quelle 1.9.3 finden konnte, ist oben: Der Fehler mit dem doppelten Namen wird unterdrückt und die Warnung vor nicht verwendeten Variablen wird unterdrückt. Abgesehen von diesen beiden Dingen _ist es nur eine einfache alte Variable wie jede andere. Ich kenne keine Dokumentation über die (geringfügige) Besonderheit von _.

In Ruby 2.0 wird der idUScore == v[i]Test in warn_unused_vardurch einen Aufruf an Folgendes ersetzt 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]));

und is_private_local_idunterdrückt Warnungen für Variablen, die beginnen mit _:

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

eher als nur sich _selbst. 2.0 lockert also die Dinge ein bisschen auf.

mu ist zu kurz
quelle
1
Ich frage mich, ob der Unterschied im Verhalten lambda { |_, _| _ }.call(4, 2)zwischen 1,8 und 1,9 dann nur eine unbeabsichtigte Nebenwirkung ist. Wie unter "normalen" Umständen, unter denen der Variablenname nicht dupliziert werden kann, spielt die Reihenfolge, in der sie zugewiesen werden, keine Rolle.
Andrew Marshall
3
@ AndrewMarshall: Ja, ich denke, das Problem "4 gegen 2" ist nur ein Artefakt dafür, wie 1.8 und 1.9 mit dem Stapel umgehen. Das einzige Mal, dass es auffällt, ist, |_,_,...|dass der Duplikatfehler unterdrückt wurde.
Mu ist zu kurz
2
@ AndrewMarshall: Ich frage mich, ob alle hinter unserem Rücken die ChangeLogs des anderen lesen.
Mu ist zu kurz
2
Schöner Fund. Ich nahm an, dass es so etwas Einfaches sein würde, nur den doppelten Parameternamenfehler zu unterdrücken. Ruby ist wirklich ein großes Durcheinander: D
Niklas B.
2
@mu: Natürlich habe ich noch keine wirklich saubere Implementierung einer interpretierten Sprache gesehen (Lua kommt näher).
Niklas B.
24

_ist eine gültige Kennung. Bezeichner können nicht nur Unterstrichen enthalten, können sie auch sein ein Unterstrich.

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

Sie können es auch als Methodennamen verwenden:

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

Natürlich ist es weder ein lesbarer Name, noch gibt es dem Leser Informationen darüber, worauf sich die Variable bezieht oder was die Methode tut.

IRBsetzt insbesondere _auf den Wert des letzten Ausdrucks:

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

Wie im Quellcode wird einfach _der letzte Wert festgelegt:

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

Habe ein Repository erkundet. Folgendes habe ich gefunden:

In den letzten Zeilen der Datei id.csteht der Aufruf:

REGISTER_SYMID(idUScore, "_");

grepDie Quelle für idUScoregab mir zwei scheinbar relevante Ergebnisse:

shadowing_lvar_genscheint der Mechanismus zu sein, durch den der formale Parameter eines Blocks eine gleichnamige Variable ersetzt, die in einem anderen Bereich existiert. Es ist die Funktion, die den Namen "duplizierter Argumentname" SyntaxErrorund die Warnung "äußere lokale Variable beschatten" auszulösen scheint .

Nachdem ich grepdie Quelle für gefunden hatte shadowing_lvar_gen, fand ich im Änderungsprotokoll für Ruby 1.9.3 Folgendes :

Di 11 Dez 01:21:21 2007 Yukihiro Matsumoto

  • parse.y (shadowing_lvar_gen): Kein doppelter Fehler für "_".

Welches ist wahrscheinlich der Ursprung dieser Linie :

if (idUScore == name) return name;

Daraus schließe ich, dass in einer Situation wie proc { |_, _| :x }.call :a, :beiner eine _Variable einfach die andere beschattet.


Hier ist das fragliche Commit . Grundsätzlich wurden diese beiden Zeilen eingeführt:

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

Aus einer Zeit, als idUScorees anscheinend gar nicht gab.

Matheus Moreira
quelle
6
Dies erklärt nicht bei allen , warum lambda { |_, _| 42 }funktioniert , während lambda { |x, x| 42 }dies nicht tut.
Andrew Marshall
@ AndrewMarshall, es scheint, Sie haben Recht. |_, _|funktioniert, aber |__, __|nicht. _scheint eine besondere Bedeutung zu haben, ich werde sehen, ob ich irgendwelche Informationen aus der Ruby-Quelle graben kann.
Matheus Moreira
1
Übrigens ist Ihr Update zwar informativ, aber nicht relevant, da es nur für IRb gilt, was ich in meiner Frage, nach der ich nicht gefragt habe, ausdrücklich angegeben habe.
Andrew Marshall
1
+1 zum Ausgraben des ChangeLog-Eintrags. Jeder sollte ein C-Hacker sein :)
mu ist zu kurz
1
+1 für den IRB-Quellcode. IRB.CurrentContext.last_value ist sehr interessant zu wissen, auch wenn es für die gestellte Frage nicht relevant ist. Ich bin hier von einer Google-Suche nach IRB-Unterstrich gelandet.
Josh