2009-11-13 22 views

Respuesta

44

ref():

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 ...

+1

Pensé que ref() solo te diría qué tipo de referencia es y no devuelve nada si no es uno. –

+3

thx - bueno, quería decirte solo thx - pero no está permitido – pm100

+7

@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. –

40

$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.

+3

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

+2

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

+0

@ysth: ¡bastante! He actualizado mi respuesta ... es raro encontrar un buen uso para 'ref' en objetos bendecidos. – Ether

14

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.

2

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.

+4

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. –

+2

Por favor, nunca, en realidad nunca pruebe los refs de esa manera. Es tan fácil hacerlo más fácil. :) –

+0

cerebro, ese es otro punto interesante, no el que estaba pensando. Puedo ver tu punto sin embargo. – daotoad

4

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.

Cuestiones relacionadas