2008-10-13 7 views
29

Ich habe einigen schrecklichen Code in Perl geschrieben gesehen, aber ich kann nicht mit dem Kopf noch Schwanz dieses machen:

select((select(s),$|=1)[0]) 

Es ist in einigem Netzwerk-Code, den wir mit einem Server zu kommunizieren, und ich nehme an, es ist etwas mit Pufferung zu tun (seit es $| setzt).

Aber ich kann nicht herausfinden, warum gibt es mehrere select Aufrufe oder die Array-Referenz. Kann mir jemand helfen?

+2

Um Menschen zu verwirren und zu frustrieren, die den Code pflegen müssen, d.h. Sie. –

Antwort

62

Es ist ein fieses kleines Idiom zum Festlegen von Autoflush auf einem anderen Dateihandle als STDOUT.

select() nimmt das mitgelieferte Dateihandle und ersetzt (im Grunde) STDOUT damit, und es gibt das alte Dateihandle zurück, wenn es fertig ist.

So umleitet (select($s),$|=1) das Dateihandle (erinnern Sie sich select gibt das alte zurück) und setzt Autoflush(). Es tut dies in einer Liste ((...)[0]) und gibt den ersten Wert zurück (der das Ergebnis des Aufrufs select ist - das ursprüngliche STDOUT) und übergibt dann das zurück an ein anderes select, um das ursprüngliche STDOUT-Dateihandle wieder herzustellen. Puh.

Aber jetzt verstehen Sie es (na ja, vielleicht;)), tun dies statt:

use IO::Handle; 
$fh->autoflush; 
+1

Nasty, warum genau? – paxdiablo

+15

@Pax: Warum? Schau es Dir an! – Dan

+1

Entschuldigung, ich dachte, du meintest so gemein wie in dubioser Funktionalität statt in mangelnder Klarheit. Also übergibt man das Ergebnis der inneren Auswahl an die äußere Auswahl und wählt das Original erneut aus. Das macht jetzt Sinn, aber du hast Recht, ich werde es wahrscheinlich verschrotten und Autoflush verwenden. – paxdiablo

8

Es ist übermäßig klug Code auf Puffer für das Drehen am Griff Spülung s und dann erneut die Auswahl des aktuellen Griff.

Weitere Informationen finden Sie unter perldoc -f select.

28

Der Weg, um herauszufinden, Code ist, um es auseinander zu nehmen. Du weißt, dass das Zeug in Klammern vor den Dingen draußen passiert. Dies ist die gleiche Art, wie Sie herausfinden würden, was Code in anderen Sprachen macht.

Das erste Bit ist dann:

(select(s), $|=1) 

Diese Liste hat zwei Elemente, die die Ergebnisse der beiden Operationen sind: man die s Dateihandle als Standard auswählen dann eine $| auf einen wahren Wert zu setzen. Die $| ist eine der pro-Dateihandle-Variablen, die nur für das aktuell ausgewählte Dateihandle gelten (siehe Understand global variables unter Der Effektive Perler). Am Ende haben Sie eine Liste aus zwei Elementen: der vorhergehende Standard-Dateihandle (das Ergebnis select) und 1.

Der nächste Teil ist eine wörtliche Liste Scheibe das Produkt in der Index ziehen 0:

(PREVIOUS_DEFAULT, 1)[0] 

Das Ergebnis davon ist das einzelne Element, das das vorherige Standarddateihandle ist.

Der nächste Teil nimmt das Ergebnis der Scheibe und nutzt es als Argument für einen anderen Anruf zu select

select(PREVIOUS_DEFAULT); 

So in der Tat Sie $| auf einem Dateihandle und am Ende wieder da, wo Sie festgelegt haben Begann mit dem Standard-Dateihandle.

10

In einem anderen Veranstaltungsort, schlug ich einmal, dass eine verständlichere Version so wäre:

for (select $fh) { $| = 1; select $_ } 

Dies bewahrt das kompakte Idioms einzigen Vorteil ist, dass keine variablen Bedürfnisse in dem umgebenden Rahmen deklariert werden.

Oder wenn Sie mit $_ nicht bequem sind, können Sie es wie folgt schreiben:

for my $prevfh (select $fh) { $| = 1; select $prevfh } 

Der Umfang der $prevfh zum for Block beschränkt ist. (Aber wenn Sie Perl schreiben, haben Sie wirklich keine Entschuldigung dafür, sich über $_ zu ärgern.)

+0

Sowohl deine als auch Randalls Versionen sind weniger als offensichtlich, aber zumindest hat Randalls den Vorteil, kurz zu sein. Gibt es einen Grund, warum meine $ old_fh = select ($ fh); $ | = 1; Wählen Sie ($ old_fh); funktioniert nicht? Als ob es die Verständlichkeit ist, die Sie anstreben, wäre eine wesentlich vernünftigere Wahl. – Dan

+1

Haben Sie irgendwelche Behauptungen darüber gesehen, dass es nicht funktioniert? In jedem Fall, wenn Sie eine vernünftige Wahl haben wollen, verwenden Sie IO :: Handle. Was die Kürze anbelangt, hat Randall einen Vorteil von 1-4 Zeichen, je nach Leerzeichen. –

+0

Das war übrigens eine ganz ehrliche Frage. Ich war noch nie in einer Position, wo ich es tun musste (zumindest wo IO :: Handle nicht verfügbar war). – Dan

20
select($fh) 

Wählen Sie einen neuen Standard-Datei-Handle. Siehe http://perldoc.perl.org/functions/select.html

(select($fh), $|=1) 

Schalten Sie Autoflushing. Siehe http://perldoc.perl.org/perlvar.html

(select($fh), $|=1)[0] 

Geben Sie den ersten Wert dieses Tupels ein.

select((select($fh), $|=1)[0]) 

select es, das heißt die alte Standard-Datei-Handle wiederherstellen.


Entspricht

$oldfh = select($fh); 
$| = 1; 
select($oldfh); 

, die in der Seite Perldoc

use IO::Handle; 
$fh->autoflush(1); 

wie gezeigt, bedeutet.

+1

Wenn jemand 'IO :: Handle' nicht verwenden möchte, dann ist es am wenigsten möglich, diese Monstrosität in eine Funktion wie' sub flush ($) {wake() (($ (0)), $ | =) einzufügen 1) [0]); } ' –

+1

Wenn Sie es in eine Funktion setzen, dann können Sie es genauso gut mit dem temporären schreiben, um den alten Griff zu halten. :) – hobbs

+0

@hobbs - Äh, ich könnte in beide Richtungen gehen. Für Funktionen, die einfach sind, sobald sie in einer Funktion sind, müssen Sie sie niemals wirklich neu betrachten, noch wird sich die Funktionalität einer "Flush" -Funktion jemals ändern. Vielleicht ist die entsetzliche Version etwas schneller. Vielleicht können Sie damit einen Praktikanten erschrecken. –

2

Es ist Überoptimierung, um das Laden von IO :: Handle zu überspringen.

use IO::Handle; 
$fh->autoflush(1); 

ist viel besser lesbar.

+0

Ich würde nicht überoptimierung sagen. mit lexikalischen Dateihandles ist ein * relativ * neues Ding –

+1

@Nathan Fellman: in der "nur ein Jahrzehnt" Sinn für neue, jedenfalls ... – hobbs

+0

wahr. Ich arbeite mit einer Codebase, die eine Reihe von Skripten von damals hat. Bis vor kurzem habe ich Dinge in Perl 4 geschrieben. –