2008-10-13 10 views
29

J'ai vu un code horrible écrit en Perl, mais je ne peux ni queue ni tête de celui-ci:

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

Il est dans un code de réseau que nous utilisons pour communiquer avec un serveur et je suppose qu'il est quelque chose à faire avec la mise en mémoire tampon (puisqu'il définit $|).

Mais je n'arrive pas à comprendre pourquoi il y a plusieurs appels select ou la référence de tableau. Quelqu'un peut m'aider?

+2

Pour confondre et frustrer les personnes qui doivent gérer le code, c'est-à-dire vous. –

Répondre

62

C'est un mauvais idiome pour définir autoflush sur un handle de fichier autre que STDOUT.

select() prend le handle de fichier fourni et (fondamentalement) remplace STDOUT par celui-ci, et il renvoie l'ancien handle de fichier quand c'est fait.

Alors (select($s),$|=1) redirige le handle de fichier (rappelez-vous select renvoie l'ancien) et définit autoflush ($| = 1). Il le fait dans une liste ((...)[0]) et renvoie la première valeur (qui est le résultat de l'appel select - le STDOUT d'origine), puis transmet cette dans un autre select pour rétablir le handle de fichier STDOUT d'origine. Phew.

Mais maintenant, vous le comprenez (bien, peut-être;)), faire à la place:

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

Nasty, pourquoi exactement? – paxdiablo

+15

@Pax: pourquoi? REGARDE ÇA! – Dan

+1

Désolé, je pensais que vous vouliez dire méchant comme dans la fonctionnalité douteuse plutôt que le manque de clarté. C'est donc passer le résultat du select interne au select externe, resélectionnant l'original. Cela a du sens maintenant, mais vous avez raison, je vais probablement l'enlever et utiliser autoflush. – paxdiablo

8

Il est un code trop intelligent pour tourner le tampon de rinçage sur la poignée s puis re-sélectionner la poignée en cours.

Voir perldoc -f select pour plus d'informations.

28

La manière de comprendre n'importe quel code est de le démonter. Vous savez que les choses entre parenthèses arrivent avant les choses à l'extérieur. C'est de la même manière que vous pourriez déterminer ce que fait le code dans d'autres langues.

Le premier bit est alors:

(select(s), $|=1) 

Cette liste comporte deux éléments, qui sont les résultats de deux opérations: l'une pour sélectionner alors que la valeur par défaut du s filehandle un pour définir $| à une vraie valeur. Le $| est l'une des variables par filehandle qui s'appliquent uniquement au handle de fichier actuellement sélectionné (voir Understand global variables à The Effective Perler). En fin de compte, vous avez une liste de deux éléments: le descripteur de fichier par défaut précédent (le résultat de select) et 1.

La partie suivante est une tranche de liste littérale pour retirer l'élément dans l'index 0:

(PREVIOUS_DEFAULT, 1)[0] 

Le résultat est l'élément unique qui est le handle de fichier par défaut précédent.

La partie suivante prend le résultat de la tranche et l'utilise comme argument à un autre appel à select

select(PREVIOUS_DEFAULT); 

Donc, en effet, vous avez mis $| sur un descripteur de fichier et a fini par là où vous commencé avec le handle de fichier par défaut.

10

Dans un autre lieu, je propose une fois qu'une version plus compréhensible serait donc:

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

Cela préserve l'avantage unique de langage compact sans besoins variables déclarés dans le périmètre environnant.

Ou si vous n'êtes pas à l'aise avec $_, vous pouvez l'écrire comme ceci:

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

La portée de $prevfh est limitée au bloc for. (Mais si vous écrivez Perl, vous n'avez vraiment aucune excuse pour être complaisant à propos de $_.)

+0

Les versions de Randall et celles de Randall sont moins qu'évidentes, mais au moins celle de Randall a l'avantage d'être courte. Y at-il une raison pour laquelle mon $ old_fh = select ($ fh); $ | = 1; select ($ old_fh); ne fonctionne pas? Comme si c'était compréhensible, cela vous paraîtrait un choix beaucoup plus judicieux. – Dan

+1

Avez-vous vu des affirmations à ce sujet ne fonctionnant pas? Dans tous les cas, si vous voulez un choix judicieux, utilisez IO :: Handle. En ce qui concerne la shortness, Randall a un avantage de 1-4 caractères en fonction des espaces. –

+0

C'était une question complètement honnête, d'ailleurs. Je n'ai jamais été dans une position où je devais le faire (au moins où IO :: Handle n'était pas disponible). – Dan

20
select($fh) 

Sélectionner une nouvelle poignée de fichier par défaut. Voir http://perldoc.perl.org/functions/select.html

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

Allumez autoflush. Voir

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

Retourne la première valeur de ce tuple.

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

select il, à savoir rétablir l'ancien descripteur de fichier par défaut.


équivalent à

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

qui signifie

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

comme démontré dans la page de perldoc.

+1

Si quelqu'un ne veut pas utiliser 'IO :: Handle', le moins qu'il puisse faire est d'envelopper cette monstruosité dans une fonction comme' sub flush ($) {select ((select ($ _ [0]), $ | = 1) [0]); } ' –

+1

Si vous allez le mettre dans une fonction alors vous pourriez aussi bien écrire le long chemin avec le temporaire pour tenir l'ancienne poignée. :) – hobbs

+0

@hobbs - Eh, je pourrais aller dans les deux sens. Pour des fonctions aussi simples, une fois dans une fonction, vous n'avez jamais vraiment besoin de la regarder à nouveau, et la fonctionnalité d'une fonction 'flush' ne changera jamais non plus. Peut-être que la version horrible est légèrement plus rapide. Peut-être que vous pouvez l'utiliser pour effrayer un stagiaire. –

2

Il est suroptimisé de ne pas charger IO :: Handle.

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

est beaucoup plus lisible.

+0

Je ne dirais pas overoptimization. en utilisant les handles de fichiers lexicaux est une chose * relativement * nouvelle –

+1

@Nathan Fellman: dans le "sens d'une décennie" nouveau, de toute façon ... – hobbs

+0

vrai. Je travaille avec une base de code qui a un certain nombre de scripts à l'époque. Jusqu'à récemment, je conservais les choses écrites en perl 4. –