Подтвердить что ты не робот

Как узнать, какой тип значения находится в переменной Perl?

Как узнать, какой тип значения находится в переменной Perl?

$x может быть скаляром, ссылкой на массив или ссылкой на хэш (или, возможно, другие вещи).

4b9b3361

Ответ 1

ref():

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

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

Ответ 2

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

Ссылка - это просто определенный тип скаляра. Встроенная функция ref скажет вам, что это за ссылка. С другой стороны, если у вас есть благословенная ссылка, ref скажет вам только имя пакета, в который была благословлена ссылка, а не фактический тип ядра данных (благословенными ссылками могут быть hashrefs, arrayrefs или другие вещи). Вы можете использовать Scalar :: Util 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 и т.д.) См. Этот вопрос: Как получить функцию Perl ref() для возврата REF, IO и 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") {...). Также см. Полиморфизм.

Ответ 3

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

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

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

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

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

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

Ответ 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.

обслуживаемой.

Ответ 5

В какой-то момент я прочитал достаточно убедительный аргумент 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;
}

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

Обновление

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

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

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

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

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