2008-10-13 18 views
29

he visto algo de código horrible escrito en Perl, pero no puedo ni pies ni cabeza de éste:

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

Es en algún código de red que se utiliza para comunicarse con un servidor y supongo que es algo que ver con el almacenamiento en búfer (ya que establece $|).

Pero no puedo entender por qué hay múltiples select llamadas o la matriz de referencia. ¿Puede alguien ayudarme?

+2

Para confundir y frustrar a las personas que tienen que mantener el código, es decir, usted. –

Respuesta

62

Es una pequeña expresión desagradable para configurar autoflush en una maneta de archivo que no sea STDOUT.

select() toma el identificador de archivo suministrado y (básicamente) reemplaza STDOUT con él, y devuelve el identificador de archivo anterior cuando termina.

Entonces (select($s),$|=1) redirige el identificador de archivo (recuerde select devuelve el anterior), y establece autoflush ($| = 1). Hace esto en una lista ((...)[0]) y devuelve el primer valor (que es el resultado de la llamada select - el STDOUT original), y luego pasa que devuelve a otro select para restablecer el identificador de archivo STDOUT original. Uf.

Pero ahora que lo entienden (bueno, tal vez;)), debe hacerse lo siguiente:

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

desagradable, ¿por qué exactamente? – paxdiablo

+15

@Pax: ¿por qué? ¡MÍRALO! – Dan

+1

Lo siento, pensé que te referías a lo desagradable como a la funcionalidad poco fiable en lugar de a la falta de claridad. Por lo tanto, está pasando el resultado de la selección interna a la selección externa, volviendo a seleccionar el original. Eso tiene sentido ahora, pero tienes razón, probablemente lo chataré y usaré autoflush. – paxdiablo

8

su código excesivamente inteligente para encender buffer de lavado en el mango s y luego volver a la selección de la manija actual.

Consulte perldoc -f select para más.

28

La manera de descubrir cualquier código es separarlo. Sabes que las cosas entre paréntesis ocurren antes que las cosas afuera. Esta es la misma forma en que averiguaba qué código está haciendo en otros idiomas.

El primer bit es entonces:

(select(s), $|=1) 

Esa lista tiene dos elementos, que son el resultado de dos operaciones: una para seleccionar el gestor de archivo s como predeterminado entonces uno para ajustar $| a un valor verdadero. El $| es una de las variables por manejador de archivos que solo se aplica a la manejador de archivos seleccionada actualmente (consulte Understand global variables en The Effective Perler). Al final, usted tiene una lista de dos elementos: el anterior gestor de archivo por defecto (el resultado de select), y 1.

La siguiente parte es una rebanada lista literal de sacar el elemento en el índice 0:

(PREVIOUS_DEFAULT, 1)[0] 

El resultado de eso es el elemento único que es el identificador de archivo predeterminado anterior.

La siguiente parte toma el resultado de la división y lo utiliza como argumento a otra llamada a select

select(PREVIOUS_DEFAULT); 

Así que, en efecto, que haya configurado un gestor de archivo $| en y terminó de vuelta donde comenzó con el identificador de archivo predeterminado.

10

En otro lugar, una vez propuso que una versión más comprensible sería así:

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

Esto preserva única ventaja del idioma compacto que no hay necesidades variables ser declaradas en el ámbito circundante.

O si no se siente cómodo con $_, se puede escribir así:

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

El alcance de $prevfh se limita al bloque for. (Pero si escribe Perl realmente no tiene excusa para mostrarse inquieto por $_.)

+0

Tanto la versión suya como la de Randall son menos que obvias, pero al menos la de Randall tiene la ventaja de ser corta ... ¿Hay alguna razón por la cual mi $ old_fh = select ($ fh); $ | = 1; select ($ old_fh); no funciona? Como si fuera comprensible, sería una opción considerablemente más sensata. – Dan

+1

¿Vio alguna reclamación sobre que no funcionaba? En cualquier caso, si realmente quiere una elección sensata, usará IO :: Handle. En cuanto a la brevedad, Randall tiene una ventaja de 1-4 caracteres dependiendo del espacio en blanco. –

+0

Esa fue una pregunta completamente honesta, por cierto. Nunca he estado en una posición donde tuve que hacerlo (al menos donde IO :: Handle no ha estado disponible). – Dan

20
select($fh) 

seleccionar un nuevo identificador de archivo por defecto. Ver http://perldoc.perl.org/functions/select.html

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

Encienda autoflush. Ver http://perldoc.perl.org/perlvar.html

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

Devuelve el primer valor de esta tupla.

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

select ella, es decir, restablecer el identificador de archivo por defecto de edad.


Equivalente a

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

que significa

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

como se demuestra en la página perldoc.

+1

Si alguien no quiere usar 'IO :: Handle' lo menos que pueden hacer es envolver esta monstruosidad en una función como' sub flush ($) {select ((select ($ _ [0]), $ | = 1) [0]); } ' –

+1

Si va a ponerlo en una función, entonces también podría escribirlo a lo largo con el temporal para mantener el antiguo. :) – hobbs

+0

@hobbs - Eh, podría ir de cualquier manera. Para funciones tan simples, una vez que está en una función, nunca más tendrá que volver a mirarla, y la funcionalidad de una función 'flush' probablemente nunca cambie. Quizás la versión horrible es un poco más rápida. Tal vez puedas usarlo para asustar a un pasante. –

2

Se sobreimpulsa la omisión de carga de IO :: Handle.

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

es mucho más legible.

+0

No diría oversimetización. usar manejadores de archivos léxicos es una cosa * relativamente * nueva –

+1

@Nathan Fellman: en el sentido de "solo una década" de nuevo, de todos modos ... – hobbs

+0

cierto. Trabajo con una base de código que tiene una cantidad de secuencias de comandos desde ese momento. Hasta hace poco, mantenía cosas escritas en perl 4. –