¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?
$x
puede ser un escalar, una referencia a una matriz o una referencia a un hash (o tal vez otras cosas).
¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?¿Cómo puedo saber qué tipo de valor hay en una variable de Perl?
$x
puede ser un escalar, una referencia a una matriz o una referencia a un hash (o tal vez otras cosas).
Perl proporciona la función
ref()
por lo que se puede comprobar el tipo de referencia antes de la eliminación de referencias una referencia ...Mediante el uso de la función
ref()
puede proteger código de programa que elimina referencias a las variables de produciendo errores cuando se usa el tipo de referencia incorrecto ...
$x
siempre es un escalar. La sugerencia es sigil $
: cualquier variable (o desreferenciación de algún otro tipo) que comience por $
es un escalar. (Consulte perldoc perldata para obtener 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 utilizar Scalar::Util 's reftype
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.), vea esta pregunta: How can I get Perl's ref() function to return REF, IO, and LVALUE? y perldoc perlref.
Nota: Usted debe no uso ref
para implementar ramas de código con un objeto bendito (por ejemplo $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";
) - si es necesario tomar ninguna decisión en función del tipo de una variable, utilice isa
(es decir if ($a->isa("My::Foo") { ...
o if ($a->can("foo") { ...
) . También vea polymorphism.
Tenga en cuenta que reftype, por definición, infringe la encapsulación, por lo que debe evitarse a menos que tenga una muy buena razón. – ysth
Si usa reftype, tenga en cuenta que devuelve undef para una no referencia, por lo que un código como 'reftype ($ x) eq 'HASH'' puede generar advertencias. (ref, por otro lado, devuelve convenientemente '' para no referencias). – ysth
@ysth: ¡bastante! He actualizado mi respuesta ... es raro encontrar un buen uso para 'ref' en objetos bendecidos. – Ether
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 quiere saber el tipo de referencia, , puede usar la rutina reftype
del Scalar::Util.
Si quiere saber si es un objeto, puede usar la rutina blessed
del 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 quiere ver que hereda algo, use isa
; y si quiere verlo, el objeto maneja un rol, use DOES
.
Si quiere saber si ese escalar en realidad solo está actuando como un escalar pero vinculado a una clase, intente 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
desde 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.
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 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 tie
maneja un archivo 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.
Por lo tanto, según el argumento, es mejor usar la tipificación de pato 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;
}
Usted debe 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 que en realidad no hago esto, pero en algunos casos que tengo. 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
actualización
me di cuenta que debería presentar mis pensamientos en este enfoque.
Este método tiene la ventaja de manejar cualquier cosa que le arroje.
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 que si se trata de una referencia hash.
No me gusta esta implementación.
Probablemente sea en el que estás pensando. Yo digo que nunca pruebes contra cadenas literales. Prueba contra prototipos: if (ref $ f eq ref {}). En cuanto a la corbata, comienzas con atada(): si obtienes un objeto, haz las cosas normales. –
Por favor, nunca, en realidad nunca pruebe los refs de esa manera. Es tan fácil hacerlo más fácil. :) –
cerebro, ese es otro punto interesante, no el que estaba pensando. Puedo ver tu punto sin embargo. – daotoad
me gusta polimorfismo en lugar de comprobar manualmente por 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 potente que la comprobación manual, como se puede reutilizar sus "controles" como lo haría con cualquier otro tipo de restricción. 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 tipo de biblioteca:
package MyApp::Types;
use MooseX::Types -declare => ['EvenNumberLessThan42'];
use MooseX::Types::Moose qw(Num);
subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 };
luego hacer Foo apoyar 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)
impresiones arg is an even number less than 42
en lugar de arg is something else
.
Maintainable.
Pensé que ref() solo te diría qué tipo de referencia es y no devuelve nada si no es uno. –
thx - bueno, quería decirte solo thx - pero no está permitido – pm100
@Chris: Correcto, entonces si la variable no es una referencia, puedes deducir que es un escalar simple del hecho de que no devuelve nada. De lo contrario, sabrá qué tipo de referencia es. –