Как узнать, какой тип значения находится в переменной Perl?Как узнать, какой тип значения находится в переменной Perl?
$x
может быть скаляром, ссылкой на массив или ссылкой на хэш (или, возможно, другие вещи).
Как узнать, какой тип значения находится в переменной Perl?Как узнать, какой тип значения находится в переменной Perl?
$x
может быть скаляром, ссылкой на массив или ссылкой на хэш (или, возможно, другие вещи).
Perl предоставляет функцию
ref()
, так что вы можете проверить ссылочный тип, прежде чем разыменования ссылки ...С помощью функции
ref()
можно защитить программный код, который разыменовывает переменный из производя ошибки при неправильном типе ссылки ...
$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.
Обратите внимание, что reftype по определению нарушает инкапсуляцию, поэтому его следует избегать, если у вас нет веской причины. – ysth
Если вы используете reftype, имейте в виду, что он возвращает undef для не ссылки, поэтому код, подобный 'reftype ($ x) eq 'HASH'', может привести к предупреждениям. (ref, с другой стороны, удобно возвращает '' для не-ссылок.) – ysth
@ysth: совершенно так! Я обновил свой ответ .. редко можно найти хорошее использование для 'ref' на благословленных объектах. – Ether
Скаляр всегда содержит один элемент. Все, что находится в скалярной переменной, всегда является скаляром. Ссылка является скалярным значением.
Если вы хотите узнать, является ли эта ссылка, вы можете использовать ref
. Если вы хотите узнать ссылочный тип, , вы можете использовать процедуру reftype
от Scalar::Util.
Если вы хотите узнать, является ли это объект, вы можете использовать процедуру blessed
от Scalar::Util. Вы все равно должны заботиться о том, что такое благословенный пакет. UNIVERSAL
имеет несколько способов рассказать вам об объекте: если вы хотите проверить, что у него есть метод, который вы хотите вызвать, используйте can
; если вы хотите увидеть, что он наследует что-то, используйте isa
; и если вы хотите увидеть, что объект обрабатывает роль, используйте DOES
.
Если вы хотите узнать, действительно ли этот скаляр действует как скаляр, но привязан к классу, попробуйте tied
.Если вы получаете объект, продолжайте свои проверки.
Если вы хотите узнать, похоже ли это на номер, вы можете использовать looks_like_number
от Scalar::Util. Если это не похоже на число, и это не ссылка, это строка. Однако все простые значения могут быть строками.
Если вам нужно сделать что-то более необычное, вы можете использовать модуль, такой как Params::Validate.
В какой-то момент я прочитал достаточно убедительный аргумент в пользу 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, скорее, является ли это хэш-ссылкой.
Мне не нравится эта реализация.
Я, наверное, тот, о котором вы думаете. Я говорю, никогда не тестируйте литературные строки. Тестирование против прототипов: if (ref $ f eq ref {}). Что касается галстука, вы начинаете с tied(): если вы получаете объект, делайте обычный материал. –
Пожалуйста, никогда, никогда не проверяйте таким образом. Это так легко сделать проще. :) –
мозг, это еще один интересный момент - не тот, о котором я думал. Я могу видеть вашу точку зрения. – daotoad
Мне нравится полиморфизм вместо того, чтобы вручную проверить что-то:
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
.
Подходимость.
Я думал, что ref() только скажет вам, что это за ссылка и ничего не возвращает, если это не одно. –
thx - ну, я хотел сказать только THX, но его не разрешено – pm100
@ Крис: Правильно, поэтому, если переменная не является ссылкой, вы можете сделать вывод, что это простой скаляр из-за того, что он ничего не возвращает. В противном случае вы узнаете, что это за ссылка. –