В Perl, как я могу кратко проверить, определена ли переменная $ и содержит ли она строку ненулевой длины?

68

В настоящее время я использую следующий Perl, чтобы проверить, определена ли переменная и содержит текст. Сначала я должен проверить defined, чтобы избежать предупреждения "неинициализированное значение":

if (defined $name && length $name > 0) {
    # do something with $name
}

Есть ли лучший (предположительно более краткий) способ написать это?

Теги:
string

9 ответов

66
Лучший ответ

Вы часто видите проверку на определенность, поэтому вам не нужно иметь дело с предупреждением об использовании значения undef (и в Perl 5.10 он сообщает вам оскорбительную переменную):

 Use of uninitialized value $name in ...

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

 {
 no warnings 'uninitialized';

 if( length $name ) {
      ...
      }
 }

В других случаях вместо данных нужно использовать некое нулевое значение. Используя Perl 5.10 определенный или оператор, вы можете дать length явную пустую строку (определенную и вернуть нулевую длину) вместо переменной это вызовет предупреждение:

 use 5.010;

 if( length( $name // '' ) ) {
      ...
      }

В Perl 5.12 это немного проще, потому что length в значении undefined также возвращает undefined. Это может показаться немного глупостью, но это радует математика, которого я, возможно, хотел быть. Это не вызывает предупреждения, поэтому причина этого вопроса существует.

use 5.012;
use warnings;

my $name;

if( length $name ) { # no warning
    ...
    }
  • 3
    Кроме того, в v5.12 и позже, length undef возвращает undef вместо предупреждения и возврата 0. В логическом контексте undef так же ложно, как 0, поэтому, если вы ориентируетесь на v5.12 или более позднюю, вы можете просто написать, if (length $name) { ... }
19

Как показывает функция mobrule, вы можете использовать следующее для небольшой экономии:

if (defined $name && $name ne '') {
    # do something with $name
}

Вы можете выполнить определенную проверку и получить что-то еще более короткое, например:

if ($name ne '') {
    # do something with $name
}

Но в случае, когда $name не определен, хотя логический поток будет работать так, как предполагалось, если вы используете warnings (и вы должны быть), вы получите следующее предупреждение:

Use of uninitialized value in string ne

Итак, если есть вероятность, что $name не может быть определен, вам действительно нужно проверить определенность в первую очередь, чтобы избежать этого предупреждения. Как указывает Sinan Ünür, вы можете использовать Scalar:: MoreUtils, чтобы получить код, который делает именно это (проверяет определенность, затем проверяет нулевую длину) из коробки, с помощью метода empty():

use Scalar::MoreUtils qw(empty);
if(not empty($name)) {
    # do something with $name 
}
13

Во-первых, поскольку length всегда возвращает неотрицательное число,

if ( length $name )

и

if ( length $name > 0 )

эквивалентны.

Если вы согласны с заменой значения undefined пустой строкой, вы можете использовать оператор Perl 5.10 //=, который назначает RHS для LHS, если не определено LHS:

#!/usr/bin/perl

use feature qw( say );
use strict; use warnings;

my $name;

say 'nonempty' if length($name //= '');
say "'$name'";

Обратите внимание на отсутствие предупреждений о неинициализированной переменной, поскольку $name присваивается пустая строка, если она undefined.

Однако, если вы не хотите зависеть от установки 5.10, используйте функции Scalar::MoreUtils. Например, вышесказанное может быть записано как:

#!/usr/bin/perl

use strict; use warnings;

use Scalar::MoreUtils qw( define );

my $name;

print "nonempty\n" if length($name = define $name);
print "'$name'\n";

Если вы не хотите clobber $name, используйте default.

  • 0
    +1 за упоминание "// =" (откуда я знаю, что это будет ответом Синан :)
  • 4
    Я бы не использовал // = в этом случае, так как это изменяет данные как побочный эффект. Вместо этого используйте немного более короткую length( $name // '' ) .
Показать ещё 3 комментария
6

В тех случаях, когда мне не важно, является ли переменная undef или равна '', я обычно суммирую ее как:

$name = "" unless defined $name;
if($name ne '') {
  # do something with $name
}
  • 0
    В Perl 5.10 это можно сократить до $name //= ""; это именно то, что опубликовал Синан.
  • 0
    И даже если у вас нет Perl 5.10, вы все равно можете написать $name ||= "";
Показать ещё 2 комментария
2

Не всегда можно делать повторяющиеся вещи простым и элегантным способом.

Просто делайте то, что вы всегда делаете, когда у вас есть общий код, который реплицируется во многих проектах:

Поиск CPAN, у кого-то может быть уже код для вас. Для этой проблемы я нашел Scalar::MoreUtils.

Если вы не оштрафовали что-то, что вам нравится на CPAN, сделайте модуль и поместите код в подпрограмму:

package My::String::Util;
use strict;
use warnings;
our @ISA = qw( Exporter );
our @EXPORT = ();
our @EXPORT_OK = qw( is_nonempty);

use Carp  qw(croak);

sub is_nonempty ($) {
    croak "is_nonempty() requires an argument" 
        unless @_ == 1;

    no warnings 'uninitialized';

    return( defined $_[0] and length $_[0] != 0 );
}

1;

=head1 BOILERPLATE POD

blah blah blah

=head3 is_nonempty

Returns true if the argument is defined and has non-zero length.    

More boilerplate POD.

=cut

Затем в вашем коде назовите его:

use My::String::Util qw( is_nonempty );

if ( is_nonempty $name ) {
    # do something with $name
}

Или, если вы возражаете против прототипов и не возражаете против дополнительных парсеров, пропустите прототип в модуле и назовите его так: is_nonempty($name).

  • 2
    Разве это не похоже на использование молотка, чтобы убить муху?
  • 4
    @ Зоран Нет. Факторинг кода, подобного этому, превосходит сложное условие, воспроизводимое во многих разных местах. Это было бы похоже на использование уколов, чтобы убить слона. @daotoad: Я думаю, что вы должны сократить свой ответ, чтобы подчеркнуть использование Scalar::MoreUtils .
Показать ещё 9 комментариев
2

Можно сказать

 $name ne ""

вместо

 length $name > 0
  • 7
    Это все равно даст вам предупреждение. Причина, по которой люди сначала проверяют определенность, состоит в том, чтобы избежать предупреждения «неинициализированное значение».
0
my %hash ; 
$hash{"what"} = "What"; 
$hash{"how"} = "How"; 
my $word = $hash{"now"}; 
print $word; 
if (! $word) {
    print "Catch Ya\n"; 
}
else {
    print $word ; 
}
-1

Как насчет

if (length ($name || '')) {
  # do something with $name
}

Это не совсем эквивалентно исходной версии, так как оно также вернет false, если $name - это числовое значение 0 или строка '0', но будет вести себя одинаково во всех остальных случаях.

В perl 5.10 (или более поздней версии) подходящий подход будет заключаться в том, чтобы использовать вместо него определенный или оператор:

use feature ':5.10';
if (length ($name // '')) {
  # do something with $name
}

Это решит, что получить длину, основанную на определении $name, а не как true, поэтому 0/'0' будет обрабатывать эти случаи правильно, но для этого требуется более новая версия perl, чем многие люди доступны.

  • 2
    Зачем уводить с разбитым решением только сказать, что оно сломано?
  • 0
    Потому что, как я уже упоминал, 5.10 является «более свежей версией perl, чем доступно многим». YMMV, но «это 99% решение, которое я знаю, что вы можете использовать, но есть лучшее решение, которое вы можете использовать, может быть, вы не можете», кажется мне лучше, чем «вот идеальное решение, но вы, вероятно, можете» не используйте его, так что вот альтернатива, с которой вы, вероятно, можете обойтись как запасной вариант ».
Показать ещё 1 комментарий
-2
if ($name )
{
    #since undef and '' both evaluate to false 
    #this should work only when string is defined and non-empty...
    #unless you're expecting someting like $name="0" which is false.
    #notice though that $name="00" is not false
}
  • 1
    К сожалению, это будет ложным, когда $ name = 0;
  • 0
    да ты прав

Ещё вопросы

Сообщество Overcoder
Наверх
Меню