Perl: не ссылка на массив при расчете размера массива - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь распутать некоторый унаследованный код, где операция выполняется над $ value, если ее размер больше x (где x - это жестко запрограммированный тип int). Вот как это выглядит в настоящее время:

if (scalar(@{$value}) > x) {
    ...
}

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

Я предположил, что он может потерпеть неудачу, если значение $ не определено, поэтому я даже дал ему || [] но безрезультатно (та же ошибка):

if (scalar(@{$value || []}) > x) {
    ...
}

Я тоже пытаюсь выяснить, зачем мне нужен @ {}? Насколько я понимаю, это оценивает значение $ value в контексте списка, чтобы скаляр мог потом убедиться, что я получу размер. Делает ли это код более устойчивым или я могу просто напрямую использовать скалярное значение $? @ {} Даже делает то, что я думаю?

Я использую Perl 5.8.

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

У вас есть два вопроса.Я отвечу на них отдельно:

Вопрос: Что делает @{}?

Когда вы пишете @{$thing}, вы разыменовываете $thing в массив,Как вы заметили, это работает только тогда, когда $thing является ссылкой на массив.(Другими операторами разыменования являются %{$thing} для хеш-ссылок и ${$thing} для скалярных ссылок.)

Вам do нужен оператор разыменования там.Учтите это:

my $arrayref = [ 'Alice', 'Bob', 'Charlie' ];
my $hashref  = { x => 4, y => 5 };
my $string   = "Hello, world";
for my $thing ($arrayref, $hashref, $string) {
    print "thing         --> ", $thing, "\n";
    print "scalar(thing) --> ", scalar($thing), "\n";
}

Вывод:

thing         --> ARRAY(0x7f3b8054e468)
scalar(thing) --> ARRAY(0x7f3b8054e468)
thing         --> HASH(0x7f3b80560678)
scalar(thing) --> HASH(0x7f3b80560678)
thing         --> Hello, world
scalar(thing) --> Hello, world

Нет смысла заставлять $thing использовать скалярный контекст. Это уже скаляр!

Вопрос: Как безопасно разыменовать скаляр?

Если вы не знаете, какой тип ссылки содержится в $thing,Вы можете использовать Ref::Util для его проверки:

use Ref::Util qw( is_arrayref is_hashref );

for my $thing ($arrayref, $hashref, $string) {
    if (is_arrayref($thing)) {
        print "array: thing         --> ", @{$thing}, "\n";
        print "array: scalar(thing) --> ", scalar(@{$thing}), "\n";
    }
    elsif (is_hashref($thing)) {
        print "hash:  thing         --> ", %{$thing}, "\n";
        print "hash:  scalar(thing) --> ", scalar(%{$thing}), "\n";
    }
    else
    {
        print "else:  thing         --> ", $thing, "\n";
    }
}

Вывод:

array: thing         --> AliceBobCharlie
array: scalar(thing) --> 3
hash:  thing         --> y5x4
hash:  scalar(thing) --> 2/8
else:  thing         --> Hello, world

Наблюдения:

  • arrayref,когда разыменовывается, становится списком его элементов.print выводит каждый элемент без разделителей: AliceBobCharlie
  • Ссылка на arrayref при разыменовании и принудительном преобразовании в скаляр становится числом элементов: 3
  • Хеш-ссылка при разыменовании, становится списком ключей и значений.print выводит каждую пару без разделителей: y5x4
  • Хэш-ссылка, когда разыменовывается и вводится в скаляр, становится строкой , где первое число - это число ключей ивторое число - количество сегментов в хеш-таблице: 2/8
0 голосов
/ 16 ноября 2018

Решит следующий код:

if ($value and ref $value eq 'ARRAY' and @$value > x) { ... }

Как правило, вы можете отменить ссылку на ARRAY, только если это ссылка на ARRAY. Поэтому необходимо убедиться, что это ссылка на ARRAY, чтобы она не работала для HASH и т. Д.

...