2010-03-17 15 views
9

"Perl Best Practices" la primera línea en la sección sobre AUTOLOAD es:¿Cuándo debería usar AUTOLOAD de Perl? En

No utilice AUTOLOAD

Sin embargo, todos los casos que se describen están tratando con OO o módulos.

Tengo un script independiente en el que algunos modificadores de línea de comando controlan qué versiones de funciones particulares se definen. Ahora sé que podría tomar los condicionales y las evaluaciones y pegarlos desnudos en la parte superior de mi archivo antes que cualquier otra cosa, pero me parece conveniente y más limpio ponerlos en AUTOLOAD al final del archivo.

¿Es esta mala práctica/estilo? Si piensas así, ¿por qué? ¿Hay otra forma de hacerlo?

Según la petición de Brian

Soy básicamente usando esto para hacer la compilación condicional en base a parámetros de línea de comandos.

No me importa una crítica constructiva.

sub AUTOLOAD { 
    our $AUTOLOAD; 

    (my $method = $AUTOLOAD) =~ s/.*:://s; # remove package name 
    if ($method eq 'tcpdump' && $tcpdump) { 
     eval q(
     sub tcpdump { 
      my $msg = shift; 
      warn gf_time()." Thread ".threads->tid().": $msg\n"; 
     } 
     ); 
    } elsif ($method eq 'loginfo' && $debug) { 
     eval q(
     sub loginfo { 
      my $msg = shift; 
      $msg =~ s/$CRLF/\n/g; 
      print gf_time()." Thread ".threads->tid().": $msg\n"; 
     } 
     ); 
    } elsif ($method eq 'build_get') { 
     if ($pipelining) { 
      eval q(
      sub build_get { 
       my $url = shift; 
       my $base = shift; 
       $url = "http://".$url unless $url =~ /^http/; 
       return "GET $url HTTP/1.1${CRLF}Host: $base$CRLF$CRLF"; 
      }  
      ); 
     } else { 
      eval q( 
      sub build_get { 
       my $url = shift; 
       my $base = shift; 
       $url = "http://".$url unless $url =~ /^http/; 
       return "GET $url HTTP/1.1${CRLF}Host: $base${CRLF}Connection: close$CRLF$CRLF"; 
      }  
      ); 
     }  
    } elsif ($method eq 'grow') { 
     eval q{ require Convert::Scalar qw(grow); }; 
     if ([email protected]) { 
      eval q(sub grow {}); 
     } 
     goto &$method; 
    } else { 
     eval "sub $method {}"; 
     return; 
    } 
    die [email protected] if [email protected]; 
    goto &$method; 
} 
+1

Un ejemplo de lo que está haciendo mejoraría la discusión. :) –

Respuesta

1

Si su única razón para usar AUTOLOAD es reubicar el bloque hasta el final, ¿por qué no ponerlo al final en una subrutina, y luego llamarlo tan pronto como se definan sus variables dependientes?

sub tcpdump; # declare your subs if you want to call without parens 

# define the parameters 

compile(); 

# code that uses new subs 

sub compile { 
    *tcpdump = $tcpdump ? sub { 
     my $msg = shift; 
     warn gf_time()." Thread ".threads->tid().": $msg\n"; 
    } : sub {}; 
    # ... 
} 
# EOF 

Mejor aún, si las variables globales no son necesarios en otro lugar, sólo tiene que pasar a los valores compile como argumentos.

+0

@Eric Strom: ¿Así que sientes que este es un estilo mejor y más limpio que el 'AUTOLOAD'? Parece ser prácticamente idéntico a lo que estoy haciendo con 'AUTOLOAD', excepto que tengo que llamar' compile' explícitamente al comienzo del programa. ¿Por qué es mejor que invocar implícitamente 'AUTOLOAD' la primera vez que se llama a una función? Solución muy interesante ya que no había visto la sintaxis '* name = sub {...}' antes. –

+1

@Robert S.Barnes => 'AUTOLOAD' lleva consigo un poco de equipaje: las complicaciones con la herencia, la velocidad, que requieren que los subs sean llamados como métodos. Esos son todos males necesarios si estás haciendo algo que realmente necesita 'AUTOLOAD', pero si no, podría ser solo una fuente de errores. Lo mejor es hacer la solución más simple que funciona. Por separado, el uso de la sintaxis anon en lugar de la evaluación de cadena permite a perl detectar errores en tiempo de compilación en lugar de en tiempo de ejecución. –

6

Creo que para una secuencia de comandos independiente, este enfoque está bien. Puede crear subrutinas sobre la marcha para acelerar las llamadas posteriores, por ejemplo .:

sub AUTOLOAD { 
    (my $name = our $AUTOLOAD) =~ s/.*:://; 
    no strict 'refs'; # allow symbolic references 

    *$AUTOLOAD = sub { print "$name subroutine called\n" };  
    goto &$AUTOLOAD; # jump to the new sub 
} 

carga automática es difícil cuando la producción de árboles de herencia.

7

Una estrategia alternativa sería escribir el script como un módulo App :: * y tener la opción de línea de comando elegir qué clase cargar para proporcionar cualquier funcionalidad que sea conectable dependiendo de la opción. Usted podría require esa clase just-in-time una vez que sepa cuál es. Es un trabajo un poco más adelantado, pero si tiene la intención de mantener el guión durante mucho tiempo, apuesto a que valdría la pena. En los últimos años se ha visto la creación de algunas herramientas extrañas para crear scripts cuya funcionalidad realmente se encuentra en módulos, incluidos App::Cmd, MooseX::Getopt y the bastard offspring of both.

+0

Suena interesante y vale la pena echarle un vistazo, aunque creo que podría ser excesivo para lo que estoy haciendo. Solo se trata de un script de 500 líneas. –

+0

500 es posiblemente alrededor de 450 demasiado. :) – hobbs

Cuestiones relacionadas