2009-11-13 5 views

ответ

44

ref():

Perl предоставляет функцию ref(), так что вы можете проверить ссылочный тип, прежде чем разыменования ссылки ...

С помощью функции ref() можно защитить программный код, который разыменовывает переменный из производя ошибки при неправильном типе ссылки ...

+1

Я думал, что ref() только скажет вам, что это за ссылка и ничего не возвращает, если это не одно. –

+3

thx - ну, я хотел сказать только THX, но его не разрешено – pm100

+7

@ Крис: Правильно, поэтому, если переменная не является ссылкой, вы можете сделать вывод, что это простой скаляр из-за того, что он ничего не возвращает. В противном случае вы узнаете, что это за ссылка. –

40

$x всегда является скаляром. Подсказка - это сигма $: любая переменная (или разыменование какого-либо другого типа), начиная с $, является скаляром. (См. perldoc perldata для получения дополнительных сведений о типах данных.)

Ссылка - это только определенный тип скаляра. Встроенная функция ref расскажет вам, что это за ссылка. С другой стороны, если у вас есть благословенная ссылка, ref сообщит вам только имя пакета, в которое была включена ссылка, а не фактический основной тип данных (блаженные ссылки могут быть hashrefs, arrayrefs или другие вещи). Вы можете использовать Scalar::Util «s reftype покажет вам, какой тип ссылки он:

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"; 

... производит вывод:

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

Для получения дополнительной информации о других типах ссылок (например, coderef , arrayref и т. д.), см. этот вопрос: How can I get Perl's ref() function to return REF, IO, and LVALUE? и perldoc perlref.

Примечание: Вы должны не использование ref реализовать код ветви с блаженной объекта (например, $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) - если вам нужно делать какие-либо решения, основанные на типе переменной, используйте isa (т.е. if ($a->isa("My::Foo") { ... или if ($a->can("foo") { ...) , Также см. polymorphism.

+3

Обратите внимание, что reftype по определению нарушает инкапсуляцию, поэтому его следует избегать, если у вас нет веской причины. – ysth

+2

Если вы используете reftype, имейте в виду, что он возвращает undef для не ссылки, поэтому код, подобный 'reftype ($ x) eq 'HASH'', может привести к предупреждениям. (ref, с другой стороны, удобно возвращает '' для не-ссылок.) – ysth

+0

@ysth: совершенно так! Я обновил свой ответ .. редко можно найти хорошее использование для 'ref' на благословленных объектах. – Ether

14

Скаляр всегда содержит один элемент. Все, что находится в скалярной переменной, всегда является скаляром. Ссылка является скалярным значением.

Если вы хотите узнать, является ли эта ссылка, вы можете использовать ref. Если вы хотите узнать ссылочный тип, , вы можете использовать процедуру reftype от Scalar::Util.

Если вы хотите узнать, является ли это объект, вы можете использовать процедуру blessed от Scalar::Util. Вы все равно должны заботиться о том, что такое благословенный пакет. UNIVERSAL имеет несколько способов рассказать вам об объекте: если вы хотите проверить, что у него есть метод, который вы хотите вызвать, используйте can; если вы хотите увидеть, что он наследует что-то, используйте isa; и если вы хотите увидеть, что объект обрабатывает роль, используйте DOES.

Если вы хотите узнать, действительно ли этот скаляр действует как скаляр, но привязан к классу, попробуйте tied.Если вы получаете объект, продолжайте свои проверки.

Если вы хотите узнать, похоже ли это на номер, вы можете использовать looks_like_number от Scalar::Util. Если это не похоже на число, и это не ссылка, это строка. Однако все простые значения могут быть строками.

Если вам нужно сделать что-то более необычное, вы можете использовать модуль, такой как Params::Validate.

2

В какой-то момент я прочитал достаточно убедительный аргумент в пользу Perlmonks, что тестирование типа скаляра с ref или reftype - плохая идея. Я не помню, кто выдвинул идею вперед или ссылку. Сожалею.

Дело в том, что в Perl существует много механизмов, которые позволяют сделать данный скалярный акт, как будто все что угодно. Если вы используете tie дескриптор файла так, чтобы он работал как хэш, тестирование с reftype скажет вам, что у вас есть файл. Он не скажет вам, что вам нужно использовать его как хэш.

Итак, аргумент пошел, лучше использовать утиную печать, чтобы узнать, что такое переменная.

Вместо:

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; 
} 

Вы должны сделать что-то вроде этого:

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; 
} 

По большей части я на самом деле не делать этого, но в некоторых случаях у меня есть. Я все еще думаю о том, когда этот подход подходит. Я думал, что брошу эту концепцию для дальнейшего обсуждения. Мне бы хотелось увидеть комментарии.

Update

я понял, что я должен выдвинуть свои мысли по поводу этого подхода.

У этого метода есть преимущество в том, чтобы обрабатывать все, что вы бросаете на него.

У него есть недостаток быть громоздким и несколько странным. Наткнувшись на это в каком-то кодексе, я заставил бы меня выпустить большой толстый «WTF».

Мне нравится идея проверки, является ли скаляр действительным как hash-ref, скорее, является ли это хэш-ссылкой.

Мне не нравится эта реализация.

+4

Я, наверное, тот, о котором вы думаете. Я говорю, никогда не тестируйте литературные строки. Тестирование против прототипов: if (ref $ f eq ref {}). Что касается галстука, вы начинаете с tied(): если вы получаете объект, делайте обычный материал. –

+2

Пожалуйста, никогда, никогда не проверяйте таким образом. Это так легко сделать проще. :) –

+0

мозг, это еще один интересный момент - не тот, о котором я думал. Я могу видеть вашу точку зрения. – daotoad

4

Мне нравится полиморфизм вместо того, чтобы вручную проверить что-то:

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 

Это гораздо более мощным, чем ручной проверки, так как вы можете использовать свои «чеки», как и любой другой тип ограничения. Это означает, что когда вы хотите обрабатывать массивы, хэши и даже числа меньше 42, вы просто пишете ограничение для «четных чисел меньше 42» и добавляете новый мультиметод для этого случая. «Код вызова» не затрагивается.

Ваша библиотека Тип:

package MyApp::Types; 
use MooseX::Types -declare => ['EvenNumberLessThan42']; 
use MooseX::Types::Moose qw(Num); 

subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 }; 

Затем сделайте поддержку Foo это (в этом определении класса):

class Foo { 
    use MyApp::Types qw(EvenNumberLessThan42); 

    multi method foo (EvenNumberLessThan42 $arg) { say "arg is an even number less than 42" } 
} 

Затем Foo->new->foo(40) печатает arg is an even number less than 42 вместо arg is something else.

Подходимость.

Смежные вопросы