Как я могу различить аргумент, который не был передан, и аргумент, который был передан с ложным значением? - PullRequest
8 голосов
/ 28 ноября 2011

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

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

Так что это двусмысленность:

sub mysub {
  my $arg1 = shift;
  if ($arg1){
    print "arg1 could have been 0 or it could have not been passed.";
  }
}

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

sub mysub {
  my $arg1 = (defined shift) || "NULL";
  if ($arg1 ne "NULL"){
    print "arg1 came in as a defined value.";
  }
  else {
    print "arg1 came in as an undefined value (or we were passed the string 'NULL')";
  }
}

Ответы [ 5 ]

16 голосов
/ 28 ноября 2011

Вот пример того, как вы можете обработать все возможные случаи:

sub mysub {
    my ($arg1) = @_;
    if (@_ < 1) {
        print "arg1 wasn't passed at all.\n";
    } elsif (!defined $arg1) {
        print "arg1 was passed as undef.\n";
    } elsif (!$arg1) {
        print "arg1 was passed as a defined but false value (empty string or 0)\n";
    } else {
        print "arg1 is a defined, non-false value: $arg1\n";
    }
}

(@_ - массив аргументов вашей функции. Сравнение с 1 здесь подсчитывает числоэлементы в массиве. Я намеренно избегаю shift, так как он изменяет @_, что потребовало бы от нас хранения исходного размера @_ где-нибудь.)

3 голосов
/ 28 ноября 2011

А как же:

sub mysub {
    my ( $arg ) = @_;

    if ( @_ == 0 ) {
        print "arg did not come in at all\n";
    } elsif ( defined $arg ) {
        print "arg came in as a defined value.\n";
    } else {
        print "arg came in as an undefined value\n";
    }
}

mysub ();
mysub ( undef );
mysub ( 1 );

Обновление : я добавил проверку, если что-то было передано вообще. Но это может быть полезно, только если вы ожидаете один параметр. Если вы хотите получить несколько параметров и хотите различать неопределенные и пропущенные параметры, возьмите хеш.

sub mysub_with_multiple_params {
    my %args_hash = @_;

    for my $expected_arg ( qw( one two ) ) {
        if ( exists $args_hash{ $expected_arg } ) {
            if ( defined $args_hash{ $expected_arg } ) {
                print "arg '$expected_arg' came in as '$args_hash{ $expected_arg }'\n";
            } else {
                print "arg '$expected_arg' came in as undefined value\n";
            }
        } else {
            print "arg '$expected_arg' did not come in at all\n";
        }
    }
}

mysub_with_multiple_params ();
mysub_with_multiple_params ( 'one' => undef, 'two' => undef );
mysub_with_multiple_params ( 'one' => 1, 'two' => 2 );

И, кстати: если вам нужно предпринять какие-либо шаги для дезинфекции ваших параметров, не делайте этого сами. Посмотрите на cpan и особенно Params :: Validate

1 голос
/ 29 января 2013

Единственный способ убедиться в этом - проверить длину @_ и посмотреть, есть ли аргумент в этом слоте.Это может показаться немного сложным, когда есть также обязательные аргументы, но это не обязательно.Вот образец, используемый во многих средствах доступа к объектам:

package Foo;

sub undef_or_unset {
    my ($self, @arg) = @_;

    return 'unset' unless @arg;
    my ($val) = @arg;

    return 'undef' unless defined $val;
    return 'defined';
}

package main;
use Test::More tests => 3;

my $foo = bless {} => 'Foo';

is($foo->undef_or_unset(), 'unset');
is($foo->undef_or_unset(undef), 'undef');
is($foo->undef_or_unset('bluh'), 'defined');
1 голос
/ 28 ноября 2011

лично мне нравится хранить undef для представления значений NULL - это соответствует тому, что все заполнители DBI / DBIx :: Class / SQL :: Abstract все делают, и риск установки в качестве строки "NULL" заключается в том, что вы случайновставьте строку, а не NULL.

Если вы используете последнюю версию Perl (5.10 или более позднюю), то проверьте операторы «определенные-или» // и //=которые особенно удобны для обработки аргументов.

относительно SQL, если вы хотите сгенерировать строку SQL, вы можете получить что-то вроде этого:

sub mysub {
  my ($args) = @_;
  my @fields = qw/ field1 field2 field3 /;
  my $sql = "INSERT INTO mytable (field1,field2,field3) VALUES (" .
   join(',', map { ("'".$args->{$_}."'") // 'NULL' ) } )
    .")";
  return $sql;
}

edit (чтобы ответить нанемного о NULL и undef):

Использование дескрипторов DBI с заполнителями:

my $sth = $dbh->prepare('INSERT INTO mytable (field1,field2,field3) '.
                        'VALUES (?,?,?)');

# undef will set a NULL value for field3 here:
$sth->execute( "VAL1", "VAL2", undef );

DBIx :: Class

DBIx :: Class - тот же принцип - передать значение undef для создания NULL в базе данных:

my $rs = My::Schema->resultset('MyTable');
my $obj = $rs->create({
   field1 => 'VAL1',
   field2 => 'VAL2',
   field3 => undef,    # will set a `NULL` value for field3 here
});
0 голосов
/ 28 ноября 2011

карта - твой друг.Попробуйте это:

function("joe",undef); # should print "joe" and "NULL"
function("max",38);    # should print "max" and "38"
function("sue",0);     # should print "sue" and "0"   

sub function {
    my($person,$age) = map { $_ // "NULL" } @_;
    print "person: $person\n";
    print "age:    $age\n";
}

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

function2(person=>"joe",age=>undef); # should print "joe" and "NULL"
function2(person=>"max",age=>38);    # should print "max" and "38"

sub function2 {
    my(%args) = map { $_ // "NULL" } @_;
    print "person: $args{person}\n";
    print "age:    $args{age}\n";
}

(Обновлено: для правильной обработки 0, а затем снова для использования оператора //).

...