2009-10-15 17 views
5

Así que he tenido una función de ucwords simple para Perl que he tenido durante un tiempo, y quería expandirla, esto es lo que se me ocurrió, así es como debería estar construyendo mis funciones para manejar opcionalmente parámetros?¿Es este el camino a seguir para construir subrutinas Perl?

original:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

extendido:

sub ucwords{ 
    if(@_[0] ne undef){#make sure some argument was passed 
     @overloads = (0,1,2,3); 
     $str = @_[0]; 
     if(@_[1] eq undef || @_[1] eq 0){ #default is to lowercase all but first 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }else{ #second parameters 
      if(!grep $_ eq @_[1], @overloads){ die("No overload method of ucwords() takes "[email protected]_[1]." as second parameter."); } 
      if(@_[1] eq 1){ $str =~ s/\b(\w)/\u$1/g;} #first letter to upper, remaining case maintained 
      if(@_[1] eq 2){ $str = lc($str); $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining to lower 
      if(@_[1] eq 3){ $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining case maintained 
      return $str; 
     } 
    }else{ 
     die("No overload method of ucwords() takes no arguments"); 
    } 
} 

Psy

+5

tl; dr, pero ¿su código no hace esto? 'join '' map {ucfirst} split/(\ s +)/$ string'? – Ether

+3

@Psytronic debe leer 'perldoc perlsub': http://perldoc.perl.org/perlsub.html –

+0

Gracias por el consejo, no estaba en particular buscando comentarios sobre la función en sí, sé que probablemente haya mejor formas de hacer el trabajo, fueron más los comentarios con respecto a la forma en que lo había construido, ya que no quiero estar haciendo las cosas mal desde el principio y repitiéndolo una y otra vez. – Psytronic

Respuesta

25

En una palabra: NO! mirada

Vamos a:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

Primero y ante todo, pero no se utiliza strict. Úselo. Es por tu propio bien.

En segundo lugar, usted no está utilizando warnings. Úselo. Es por tu propio bien. Por ejemplo, se debe hacer referencia al primer elemento de @_ usando $_[0] y no@_[0].

En tercer lugar, usted debe conseguir en el hábito de la lectura de la lista de preguntas frecuentes de vez en cuando antes de volver a inventar la rueda de nuevo: Ver How do I capitalize all the words on one line?

Si usted piensa que esto es duro, tenga en cuenta el hecho de que cuando se llama como:

print ucwords("FRED AND BARNEY'S LODGE"), "\n"; 

sus salidas de código

 
Fred And Barney'S Lodge 

que es el ejemplo dado en esa pregunta.

Además, tener una función que hace más de una cosa, elige lo que hace sobre la base de números misteriosos y ninguna de esas cosas correctas es una buena estrategia de diseño.

En su lugar, debe tener múltiples funciones, nombradas de forma que puedan ser entendidas por un lector casual de su código, cada una de las cuales hace una sola cosa y lo hace correctamente.

Por último, la versión extendida de su función (sin decir nada acerca de la conveniencia de escribir una función de este tipo) puede ser mejor escribir como:

# untested code follows 

use Carp; 

{ 
    my %modes = map {$_ => undef} 0 .. 3; 
    sub ucwords{ 
     croak 'No arguments passed' unless @_; 

     my ($str, $mode) = @_; 
     $mode = 0 unless defined $mode; 

     croak "Invalid mode: '$mode'" unless exists $modes{$mode}; 

     if ($mode == 0) { 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
     } 
     elsif ($mode == 1) { 
      $str =~ s/\b(\w)/\u$1/g;   
     } 
     elsif ($mode == 2) { 
      $str = lc($str); 
      $str =~ s/(\w)\b/\u$1/g;   
     } 
     else { 
      $str =~ s/(\w)\b/\u$1/g; 
     } 

     return $str; 
    } 
} 

Véase también Why use if-else if in C++?

+4

Creo que deberías haber respondido dos veces en este caso. Una vez con su respuesta original y una vez con la versión actual. Me hubieras ganado dos upvotes en ese caso. – innaM

+0

Estoy de acuerdo en que debería ser otra respuesta, pero no sé si hubiera votado el primero, simplemente diciendo que usar estrictas y advertencias realmente no está señalando los otros problemas con la función. De cualquier manera, +1 para este. :) – NateDSaint

+1

No hace falta decirlo ... pero como la persona que mantiene las secuencias de comandos en mi trabajo, lo voy a decir ... Si tuviera que usar algo como la función extendida anterior, DOCUMENTE. Nombra tus modos, aunque solo sea en los comentarios. – Rini

4

Puede ser que usted encontrará Params::Validate útil. Se puede usar para validar params por varias reglas. Aquí es como puede ser observada en su caso:

## somewhere is the start of the module 
use Params::Validate qw(:all); 

sub ucwords { 
    ## this line helps to undestand which parameter should be passed to function 
    my ($string, $algorithm_id) = @_; 

    ## make sure that 2 scalar parameters passed 
    validate_pos(@_, {'type' => SCALAR}, {'type' => SCALAR}); 

    ## main code here 
} 
11

no utilice el constructo $foo ne undef. Los operadores en Perl son lo que se conoce como "sensible al contexto". Al usar ciertos operadores, introduce ciertos contextos.ne, eq, lt, gt, le, ge son todos los operadores "cadena", el tratamiento de los escalares en cada lado como cadenas, mientras que ==, !=, <, >, <=, >= son operadores numéricos, tratando el objeto en cada lado como un número.

Sin embargo, si se está probando para la UNDEF, lo que realmente no tiene sentido que algo no definido es un número o una cadena, por lo que tienen un operador sólo para ese tipo de prueba: defined

Puede prueba si algo se define simplemente por hacer

if (defined $foo) { 
    # my cool logic on $foo here 
} 
5

esto puede ser simplemente mi opinión, y su estilo de codificación es totalmente de usted, pero personalmente me parece mucho valor en la asignación de los argumentos de las variables de la derecha del palo , y en lugar de envolver la parte "comercial" de su subrutina en un bloque if, tendría la función croak antes de e eso. Por ejemplo:

use Carp; 

sub ucwords { 
    my $str = shift; 
    defined($str) 
     or croak 'No overload method of ucwords() takes no arguments'; 
    #the rest of your logic 
} 
+0

Ese es un gran punto. Editaría el original, pero eso hace que tu comentario parezca inútil. Le agregaré una edición. – NateDSaint

+1

@NateDSaint: me tomé la libertad de editar tu publicación y eliminar mi comentario. Siéntase libre de deshacer si no le gustan los cambios. –

+0

Probablemente menos engañoso de esta manera. ¡Gracias! – NateDSaint

4

die

die, al igual que otras órdenes internas del Perl, no necesita, y por lo general no debe tener paréntesis. Sin embargo, die tiene un hermano mayor que la mayoría de la gente usa estos días, llamado

croak

Do:

use Carp; 

y luego

croak "My error here!"; 

croar funciona igual que mueren, pero en general agrega más información útil al mensaje de error que die, como la línea en la que ocurrió el error en rel a la persona que llama.

3

indización de matrices

de acceso a conjunto, al igual que otras cosas en Perl, es sensible al contexto. Piense en el sigilo que se adjunta al nombre como un "recordatorio" sobre lo que intenta acceder o usar en este momento. Cada vez que vea $, eso significa que está tratando de obtener un único valor escalar. Cada vez que veas un @, significa que estás accediendo a una lista, y % por supuesto significa un par de hash de clave/valor. Por lo tanto, cuando se accede a la matriz de la siguiente manera:

@_[1] 

Usted está pidiendo una lista, que contiene un solo elemento. Esta función le permite obtener múltiples valores de una matriz a la vez, pero cuando solo accede a un valor, causa problemas en algunos contextos, como la asignación. Por lo tanto, cuando se accede a un solo elemento de la matriz, que desea utilizar siempre el contexto escalar:

$_[1] 
5

sentencia switch de Perl: dada/cuando

Perl, a partir de 5.10 y superior, tiene un interruptor fantástica declaración integrada, llamada [given].Esto es más o menos equivalente a la declaración switch en C, pero mucho más versátil. Para activar esta función, es necesario agregar una línea en la parte superior de su script:

use 5.010; 

Esto permite a todos los Perl 5.10 características, incluyendo el interruptor (y say, que funciona como print pero automáticamente añade un "\ n "al final) se puede utilizar la siguiente manera:.

my $foo = get_foo(); 
my $nothing = 0; 
given($foo) { 
    when (undef) { say "got an undefined value!"; } 
    when ([1,3,5,6,8]) { say "was 1, 3, 5, 6, or 8"; } 
    when (/^abc/) { say "was a string starting with abc"; } 
    when ($_ == 4) { say "It was 4!!!"; } 
    when ($_ > 100) { say "Greater than 100"; } 
    default { $nothing = 1; } 
} 

la variable pasada a asigna automáticamente obtiene pone en $_ dentro del código determinado, lo que permite comparar en contra de ella. A continuación, el constructo when hace un partido inteligente contra $_. Así, en su caso, se vería como esto (la fijación de la @[] a $ [] emisión):

given ($_[1]) { 
    when (1) { $str =~ s/\b(\w)/\u$1/g } 
    when (2) { $str = lc($str); $str =~ s/(\w)\b/\u$1/g } 
    when (3) { $str =~ s/(\w)\b/\u$1/g; } 
    default { croak "No overloaded method of ucwords() takes '$_'." } 
} 
5

@_ Desembalaje

En general, siempre desea desempaquetar @_ antes de realizar cualquier otro procesamiento en su subrutina. Esto lo hace mucho, mucho más claro para los usuarios, otros mantenedores y usted mismo en el futuro sobre cómo usar su submarino. Al usar @_ directamente, es muy difícil averiguar qué se debe pasar, solo por los argumentos dados. No tienen ningún nombre significativo, por lo que es aún más difícil determinar su propósito, y usted tiene constantes mágicas en todas partes, ¡por lo general es algo malo en general!

Su mejor opción es obtener las variables en escalas con nombres significativos de inmediato, antes de hacer cualquier otra cosa.

Para subrutinas de un argumento, una solución común es usar shift. Esto saca el primer elemento de una matriz y lo devuelve (algo así como lo opuesto a pop). Si no se le asigna una matriz, y usted está en una subrutina, la saca de la matriz @_. Por lo tanto, puede hacer

sub mysub { 
    my $foo = shift; 
} 

para cualquier argumento subrutina.

Sin embargo, ¿y si tiene más? ¡Enumera la asignación de contexto, para el rescate! Es posible asignar muchas variables a la vez, usando una asignación de lista. Usted puede hacer

sub myothersub { 
    my ($foo, $bar, $baz) = @_; 
} 

Y $foo, $bar y $baz se le asigna el valor de las 0, 1, 2 y los índices de @_, respectivamente. Bueno, ¿qué pasa si no hay nada en el índice 0, 1 o 2? Todavía se asignan - se convierten en undef! Entonces puedes verificar undef como se menciona en otra parte de esta pregunta.

2

No me gustan las funciones excesivamente inteligentes. Una función excesivamente inteligente es aquella cuyo comportamiento ha cambiado totalmente por sus parámetros. Mira el tuyo, casi no comparten ningún código, excepto el manejo de parámetros. De todos modos, si me gustaría hacer algunos similares este me gustaría escribir algo como esto:

use Carp; 

{ 
    my %ucwords = (
     0 => sub { 
      my $str = lc(shift()); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     1 => sub { 
      my $str = shift; 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     2 => sub { 
      $str = lc(shift()); 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     }, 
     3 => sub { 
      my $str = shift; 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     } 
    ); 

    sub ucwords { 
     my ($str, $mode) = @_; 
     croak "No overload method of ucwords() takes no arguments" 
      unless defined $str; 
     $mode = 0 unless defined $mode; 
     my $code = $ucwords{$mode}; 
     croak "Invalid mode: '$mode'" unless defined $code; 
     goto \&$code; 
    } 
} 
2

algo que se ha insinuado, pero no se aborda directamente en otras respuestas es el uso de los modos numérico, una convención ajena a Perl llevó a cabo el relevo de C Rápido, sin mirar el código, ¿qué hace el modo # 3? Demonios, mirando el código, ¿qué hace el modo # 3?

Perl tiene cadenas eficientes y fáciles de usar. Usalos, usalos a ellos.Dale a tus modos nombres que tengan algo que ver con lo que está haciendo. Algo así como ... primero, último, recapitulación_primero, recase_last. No tienen que ser totalmente descriptivos, low_case_then_uc_last_letter sería demasiado largo para escribir, pero son suficientes para que el cerebro humano se enganche y se asocie.

Pero realmente estas son cuatro subrutinas. Las banderas de modo son banderas rojas, especialmente cuando la mayor parte de su código termina dentro de una instrucción if/else.

Cuestiones relacionadas