Проверьте, вернул ли sub undef

Надуманный пример:

use strict;
use warnings;

my $myval = 'a';
my @result = my_sub($myval);
if (@result) {
        print "DEFINED\n";
}
my ($res1, $res2, $res3) = @result;
print "res1=$res1, res2=$res2, res3=$res3\n";
sub my_sub {
         my $myval = shift;
        if ($myval eq 'a') {
                return undef;
        }
        return ("a","b","c");
}

Как мне проверить, вернул ли sub undef?

или же

Как проверить, не вернул ли sub undef?

Используйте return () или просто return вместо return undef.

Håkon Hægland 06.08.2018 21:49

Используйте только return;. Он автоматически вернет undef в скалярном контексте и () в контексте списка. Добавление аргумента к возврату может вызвать труднодоступные ошибки.

shawnhcorey 07.08.2018 14:06
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
3
84
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

return undef в контексте списка возвращает список из одного элемента, которым является undef.

 @result = my_sub($myval);
 if (@result == 1 && !defined($result[0])) {
     warn "my_sub() returned undef";
 } else {
     print "my_sub() returned data\n";
 }

Тем не менее, список с одним элементом undef почти никогда не является тем, что вам нужно. См. Как мне ничего не вернуть из подпрограммы?. Обычно вы просто хотите использовать return без аргументов. В скалярном контексте это возвращает undef, а в контексте списка - пустой список.

sub my_other_sub {
     my $myval = shift;
    if ($myval eq 'a') {
            return;
    }
    return ("a","b","c");
}
...
@result = my_other_sub($arg1);
$result = my_other_sub($arg2);
if (@result == 0) {     # or: if (!@result) ...  or: unless (@result) ...
    warn "my_other_sub(arg1) did not return any data";
} else {
    print "my_other_sub(arg1) returned data\n";
}
if (!defined($result)) {
    warn "my_other_sub(arg2) did not return any data";
} else {
    print "my_other_sub(arg2) returned data\n";
}

Подпрограмма возвращает список скаляров в контексте списка, который вы используете, а затем назначаете переменным. Итак, если вы вернете из него undef, переменные $res будут undef - сначала назначены undef, а другие не назначены (в то время как массив @result будет иметь один элемент, undef)

perl -Mstrict -wE'
    my $val = shift; 
    sub t { return undef if shift eq "bad"; return qw(a b) }; 
    my ($v1, $v2) = t($val); 
    if (not defined $v1 and not defined $v2) { say "undef" } 
    else { say "$v1, $v2" }
' bad

Первый shift берет значение из @ARGV, поэтому измените ввод «Плохо», чтобы увидеть другие случаи. Это можно было бы написать более компактно и яснее, если бы мы знали, как это использовать.

Я понимаю, что это тестовый пример, но он все еще слишком запутан и допускает сложные крайние случаи; например, ваш первый случай не будет работать, поскольку @result - это не "ложь" (пустой список), на что проверяет код, поскольку он имеет один элемент, которым является undef.

Для таких «специальных» возвратов либо используйте возвращаться без аргументов, либо бросьте умри в ситуациях, которые вы считаете исключительными. Для контекстно-зависимых возвратов см. хочу.

Я бы рекомендовал, как и другие, использовать return или более явный return (). Это возвращает пустой список. Однако, поскольку пример надуманный, мы не можем быть уверены, что пустой список не является допустимым возвратом в противном случае. Если это действительный возврат или это может быть разумно в один прекрасный день, тогда у вас есть другие варианты, которые, IMO, менее идеальны, но могут быть более гибкими.

Очевидный вариант - использовать die, как предлагал zdim, но это может быть относительно тяжеловесно. На самом деле это может быть именно то, что вы хотите - если эта ситуация действительно не должна произойти, die идеально подходит, так как это может привести к прерыванию вашей программы, если вы не заключите сбой в eval.

Другой альтернативой является возвращение вашей подпрограммы ссылки на массив вместо списка. И затем вы можете напрямую вернуть undef, ваш вызывающий сможет легко это проверить: my $result = my_sub(...);. Для других применений этого массива просто потребуется разыменование, например, my ($res1, $res2, $res3) = @$result;. Это, наверное, я предпочитаю, когда простого return () недостаточно. Бонус в том, что обратно передается только ссылка, а не весь список. Подумайте о том, чтобы сделать это, даже если пустой список недействителен, но список может быть очень большим.

Есть и другие варианты, хотя они, вероятно, самые простые. Вы можете, например, вернуть хэш (или массив), в котором одна запись указывает успех / неудачу, а другая - массив. Вы можете вернуть успех / неудачу в качестве первого элемента (ваш надуманный пример будет return (1, "a", "b", "c"), и вы должны сдвинуть этот первый элемент, чтобы увидеть, был ли он успешным или нет). Вы можете встроить свой доход в объект, который все это инкапсулирует. Из них я бы серьезно рассмотрел только один объект, но он будет сильно зависеть от остальной архитектуры и будет очень редким.

Другие вопросы по теме