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


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

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

 53
Author: brian d foy, 2009-11-13

5 answers

Ref.():

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

Utilizando la función ref() puede proteger el código de programa que desreferencie variables de producir errores cuando se utiliza el tipo incorrecto de referencia...

 49
Author: ctd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-04 19:31:57

$x es siempre un escalar. La pista es el sello $: 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 integrada 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 la referencia fue bendecida, no el nombre real tipo de núcleo de los datos (las referencias bendecidas pueden ser hashrefs, arrayrefs u otras cosas). Puedes usar Scalar:: Util 's reftype te 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 sobre los otros tipos de referencias (por ejemplo, coderef, arrayref, etc.), consulte esta pregunta: ¿Cómo puedo hacer que la función ref() de Perl devuelva REF, IO y LVALUE? y perldoc perlref.

Nota: Usted debe no use 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";) if si necesita tomar alguna decisión basada en el tipo de una variable, use isa (es decir, if ($a->isa("My::Foo") { ... o if ($a->can("foo") { ...). Ver también polimorfismo.

 42
Author: Ether,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:21

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

Si quieres saber si es una referencia, puede utilizar ref. Si quieres saber el tipo de referencia, puedes usar la rutina reftype desde Escalar::Util.

Si quieres saber si es un objeto, puedes usar la rutina blessed desde Escalar::Util. Sin embargo, nunca debería importarte lo que es el paquete bendito. UNIVERSAL tiene algunos métodos para informarle sobre un objeto: si desea comprobar que tiene el método que desea llamar, use can; si desea ver que hereda de algo, use isa; y si desea ver que el objeto maneja un rol, use DOES.

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

Si quieres saber si se parece a un número, puedes usar looks_like_number from 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 necesitas hacer algo más sofisticado, puedes usar un módulo como Params::Validate.

 17
Author: brian d foy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-09 18:58:47

Me gusta el polimorfismo en lugar de comprobar 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 comprobación manual, ya que puede reutilizar sus "comprobaciones" como lo haría con cualquier otra restricción de tipo. Eso significa que cuando desee manejar matrices, hashes y números pares menores de 42, simplemente escriba una restricción para "números pares menores de 42" y agregue 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 };

Entonces haz 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" }
}

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

Mantenible.

 4
Author: jrockway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-15 10:07:19

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

El punto era que en Perl hay muchos mecanismos que hacen posible hacer que un escalar determinado actúe como casi cualquier cosa que quieras. Si tie un filehanle para que actúe como un hash, la prueba con reftype le dirá que tiene un filehanle. No digo que necesitas usarlo como un hachís.

Entonces, el argumento fue, es mejor usar duck typing para averiguar 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 su mayor parte no hago esto, pero en algunos casos lo hago. Todavía estoy decidiendo cuándo este enfoque es apropiado. Pensé en lanzar el concepto de mayor discusión. Me encantaría ver comentarios.

Actualización

I me di cuenta de que debí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, en lugar de si es un hash ref.

No me gusta esta implementación.

 2
Author: daotoad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-13 21:19:30