Неопределенный массив в Perl - PullRequest
3 голосов
/ 27 марта 2012

Можно ли каким-то образом указать, является ли массив в Perl неопределенным или нулевым? Я сталкиваюсь с ситуациями, когда я хотел бы иметь возможность различать пустой массив и тот, который еще не был заполнен (или по какой-то причине не был заполнен).

Так, например, со ссылкой на массив я могу сделать это:

my $apples;
$apples = get_apples();
if(defined $apples){

if(scalar @$apples == 0){
# We successfully got the list of apples, but there were none
}

}else{

# There was a problem getting the list of apples

}

Моя единственная жалоба по этому поводу состоит в том, что "my $ apple" не говорит вам, что $ apple предназначен для ссылки на массив, поэтому @apples будет более конкретным.

Не похоже, что есть способ сделать что-то с массивом явно, так ли это? Будет ли всегда требоваться другая переменная, чтобы указать, был ли массив успешно заполнен?

my @apples;
(@apples) = get_apples();

Никогда нельзя проверять на успешное возвращение яблок, верно? Или я что-то упустил?

Edit: Я знаю, что get_apples может возвращать как успех, так и список для заполнения массива, но мне любопытно, есть ли способ указать нулевое или неопределенное значение только с помощью массива.

Ответы [ 6 ]

7 голосов
/ 27 марта 2012

В Perl нет разницы между пустым массивом и неинициализированным массивом.

$ perl -MDevel::Peek -e 'print Dump(\@a)'
SV = RV(0x20033b00) at 0x20033af0
  REFCNT = 1
  FLAGS = (TEMP,ROK)
  RV = 0x20091830
  SV = PVAV(0x200350c0) at 0x20091830
    REFCNT = 2
    FLAGS = ()
    ARRAY = 0x0
    FILL = -1
    MAX = -1
    ARYLEN = 0x0
    FLAGS = (REAL)

$ perl -MDevel::Peek -e '@a=(); print Dump(\@a)'
SV = RV(0x20033b00) at 0x20033af0
  REFCNT = 1
  FLAGS = (TEMP,ROK)
  RV = 0x20091818
  SV = PVAV(0x200350c0) at 0x20091818
    REFCNT = 2
    FLAGS = ()
    ARRAY = 0x0
    FILL = -1
    MAX = -1
    ARYLEN = 0x0
    FLAGS = (REAL)

Ваша единственная надежда может состоять в том, чтобы проверить атрибут MAX внутреннего объекта AV, чтобы определить,массив, содержащий любые данные:

use B;
@b = ();
@c = (1..100); @c = ();
print B::svref_2object(\@b)->MAX;      # -1
print B::svref_2object(\@c)->MAX;      # 99
6 голосов
/ 27 марта 2012
  • Можно ли каким-то образом указать, является ли массив в Perl неопределенным или нулевым?

Нет.Массивы могут быть только пустыми или содержать скаляры.

Есть лучший способ сделать то, что вы хотите: выбросить исключение.Разделение кодов ошибок и возвращаемых значений было ошибкой со времен C. Это усложняет использование функции и приводит к появлению большего количества ошибок.Исключения легко решают эту проблему, и вам не нужно перелистывать свой код проверкой ошибок (или, скорее всего, забыть).

sub get_apples {
    ...
    die "How do you like them apples?" if $it_didnt_work;
    return @apples;
}

# If get_apples() fails, the program throws an error.  Good, that
# should be the default behavior.
my @apples = get_apples();

# Or maybe you want to do something with the error.
my @apples = eval { get_apples() };
if( $@ ) {
    ...handle the error...
}
0 голосов
/ 12 сентября 2013

Вы можете вернуть одноэлементный массив, содержащий значение undef, чтобы обозначить ошибку, а затем протестировать так:

my @apples = get_apples();
if (@apples) {
    if (defined $apples[0]) {
        # you have apples
    } else {
        # some error occurred
    }
} else {
    # no apples
}
0 голосов
/ 27 марта 2012

Даже если Perl может определить разницу между неинициализированным и пустым массивом (чего он не может), это не поможет вам определить, вернул ли get_apples ошибку, потому что у вас не было бы способа заставить my @apples = get_apples() неВыполните присваивание, когда произошла ошибка.

Возможно, вы ошибаетесь в том, что return @a возвращает массив.Подпрограммы не могут возвращать массивы.Они могут вернуть только 0 или более скаляров.return @a возвращает результат @a, который является содержимым массива в контексте списка.

Невозможно отличить нулевые элементы, возвращенные из-за ошибки, от успешного ответа нулевого элемента черезвозвращаемые значения.(Конечно, вы можете использовать внеполосный канал, такой как исключение или глобальная переменная.)

Поскольку подпрограммы могут возвращать только список скаляров, вы можете сделать только две вещи:

  • Подсчитайте количество возвращенных скаляров.
  • Проверьте возвращенные скаляры.

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

При возврате ссылки на массив проверяется, определено ли возвращаемое значение.

Можно сделать что-то подобное, если вернулось первое значение (если таковые имеются) успех всегда будет определяться, но это довольно уродливо.

sub apples {
    if (...error...) {
       return undef;
    } else {
        return ...;
    }
}

my @apples = apples();
if (@apples && !defined($apples[0])) {
   ... an error occurred...
}

Я рекомендую против этого.

0 голосов
/ 27 марта 2012

Как насчет использования ref()?

my $apples;

print 'what type of object is $apples? ' . ref($apples) . $/;

$apples = get_apples();

print 'what type of object is $apples now? ' . ref($apples) . $/;

sub get_apples {
    my $empty_apple_array = [];
    return $empty_apple_array;
}

при первом создании $ яблок ref() ничего не возвращает, потому что это еще ни к чему не относится.

Затем мы делаем ссылку на пустой массив. Теперь ref() знает, что это ссылка на массив, даже если она пустая.

0 голосов
/ 27 марта 2012

Причина, по которой вы можете выполнить my $apples;, заполнить @$apples в подпрограмме get_apples(), а затем выполнить if(@$apples==0), заключается в автовивификации скаляров.

Как указывает моб, для массивов это не работает.

Чтобы обойти это, можно get_apples() передать ссылку на хеш (или, если вы хотите быть более Enterprisey, GetAppleReturn объект), который в псевдокоде выглядит как

{
  success=>1,# or 0 if it failed
  apples=>[$apple1,$apple2,...] #array reference of apples
}

Итак, тогда вы можете сделать:

my @apples;
my $rv=get_apples();
if($rv->{success})
{
  if(scalar(@{$rv->{apples}})==0)
  {
    print "Success, but no apples.\n";
  }
  else
  {
    #do whatever
  }
}
else
{
  print "There was a problem getting the apples.  How do ya like them apples?\n";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...