tutorial strawberry logo historia perl

strawberry - ¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?



perl tutorial (5)

En algún momento leí un argumento razonablemente convincente sobre Perlmonks que probar el tipo de escalar con ref o reftype es una mala idea. No recuerdo quién propuso la idea ni el enlace. Lo siento.

El punto es que en Perl hay muchos mecanismos que hacen posible que un escalar determinado actúe como cualquier cosa que desee. Si vincula un manejador de archivos para que actúe como un hash, las pruebas con reftype le indicarán que tiene un archivo de archivo. No te dirá que debes usarlo como un hash.

Entonces, según el argumento, es mejor usar la tipificación de pato para descubrir qué es una variable.

En lugar de:

sub foo { my $var = shift; my $type = reftype $var; my $result; if( $type eq ''HASH'' ) { $result = $var->{foo}; } elsif( $type eq ''ARRAY'' ) { $result = $var->[3]; } else { $result = ''foo''; } return $result; }

Deberías hacer algo como esto:

sub foo { my $var = shift; my $type = reftype $var; my $result; eval { $result = $var->{foo}; 1; # guarantee a true result if code works. } or eval { $result = $var->[3]; 1; } or do { $result = ''foo''; } return $result; }

En general, no hago esto, pero en algunos casos lo hago. Todavía estoy decidiendo cuándo este enfoque es apropiado. Pensé que lanzaría el concepto para una discusión más profunda. Me encantaría ver comentarios

Actualizar

Me di cuenta de que debería presentar mis pensamientos sobre este enfoque.

Este método tiene la ventaja de manejar cualquier cosa que le arrojes.

Tiene la desventaja de ser engorroso y algo extraño. Tropezar con esto en algún código me haría emitir un gran ''WTF''.

Me gusta la idea de probar si un escalar actúa como un hash-ref, más bien si se trata de una referencia hash.

No me gusta esta implementación.

¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?

$x podría ser un escalar, una referencia a una matriz o una referencia a un hash (o tal vez otras cosas).


Me gusta el polimorfismo en lugar de verificar manualmente algo:

use MooseX::Declare; class Foo { use MooseX::MultiMethods; multi method foo (ArrayRef $arg){ say "arg is an array" } multi method foo (HashRef $arg) { say "arg is a hash" } multi method foo (Any $arg) { say "arg is something else" } } Foo->new->foo([]); # arg is an array Foo->new->foo(40); # arg is something else

Esto es mucho más poderoso que la verificación manual, ya que puede reutilizar sus "cheques" como lo haría con cualquier otra restricción de tipo. Eso significa que cuando quiere manejar matrices, hash e incluso números menores que 42, simplemente escribe una restricción para "números pares menores que 42" y agrega un nuevo multimétodo para ese caso. El "código de llamada" no se ve afectado.

Su biblioteca de tipos:

package MyApp::Types; use MooseX::Types -declare => [''EvenNumberLessThan42'']; use MooseX::Types::Moose qw(Num); subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 };

Luego haga que Foo apoye esto (en esa definición de clase):

class Foo { use MyApp::Types qw(EvenNumberLessThan42); multi method foo (EvenNumberLessThan42 $arg) { say "arg is an even number less than 42" } }

Entonces Foo->new->foo(40) imprime arg is an even number less than 42 lugar de arg is something else .

Maintainable.


Un escalar siempre contiene un solo elemento. Lo que sea que esté en una variable escalar siempre es escalar. Una referencia es un valor escalar.

Si quiere saber si es una referencia, puede usar ref . Si desea conocer el tipo de referencia, puede usar la rutina reftype de Scalar::Util .

Si desea saber si se trata de un objeto, puede usar la rutina blessed de Scalar::Util . Aunque nunca deberías preocuparte por el paquete bendito. UNIVERSAL tiene algunos métodos para informarle acerca de un objeto: si desea verificar que tiene el método que desea llamar, use can ; si quieres ver que hereda de algo, usa isa ; y si quiere verlo, el objeto maneja un rol, use DOES .

Si quieres saber si ese escalar realmente solo está actuando como un escalar pero vinculado a una clase, prueba a tied . Si obtiene un objeto, continúe con sus cheques.

Si desea saber si se parece a un número, puede usar looks_like_number de Scalar::Util . Si no se ve como un número y no es una referencia, es una cadena. Sin embargo, todos los valores simples pueden ser cadenas.

Si necesita hacer algo más elegante, puede usar un módulo como Params::Validate .


ref() :

Perl proporciona la función ref() para que pueda verificar el tipo de referencia antes de desreferenciar una referencia ...

Al utilizar la función ref() puede proteger el código del programa que desreferencia las variables de la producción de errores cuando se utiliza el tipo de referencia incorrecto ...


$x es siempre un escalar. La sugerencia es sigil $ : cualquier variable (o desreferenciación de algún otro tipo) que comience con $ es un escalar. (Ver perldoc perldata para más información sobre los tipos de datos).

Una referencia es solo un tipo particular de escalar. La función incorporada ref le dirá qué tipo de referencia es. Por otro lado, si tiene una referencia bendecida, ref solo le dirá el nombre del paquete en el que se bendijo la referencia, no el tipo de núcleo real de los datos (las referencias benditas pueden ser hashrefs, arreglos de matriz u otras cosas). Puede usar el tipo de referencia de Scalar::Util le dirá qué tipo de referencia es:

use Scalar::Util qw(reftype); my $x = bless {}, ''My::Foo''; my $y = { }; print "type of x: " . ref($x) . "/n"; print "type of y: " . ref($y) . "/n"; print "base type of x: " . reftype($x) . "/n"; print "base type of y: " . reftype($y) . "/n";

... produce la salida:

type of x: My::Foo type of y: HASH base type of x: HASH base type of y: HASH

Para obtener más información acerca de los otros tipos de referencias (por ejemplo, coderef, arrayref, etc.), consulte esta pregunta: ¿Cómo puedo obtener la función ref () de Perl para devolver REF, IO y LVALUE? y perldoc perlref .

Nota: No debe usar ref para implementar ramas de código con un objeto bendecido (por ejemplo, $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined"; ) - si necesita tomar decisiones basadas en el tipo de una variable, use isa (es decir, if ($a->isa("My::Foo") { ... o if ($a->can("foo") { ... ). También vea el polymorphism .