如何区分未传递参数和传递了一个 false 值的参数?

9
我正试图找出在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个回答

17
以下是处理所有可能情况的示例:

以下是处理所有可能情况的示例:

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,因为它会改变 @_,这将需要我们在某个地方存储原始大小的 @_。)


谢谢,这很好而且详尽。我没有考虑到接收未定义值和不接收值之间的区别(在我的情况下不相关,但考虑这一点很好)。 - Stephen

3

关于:

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


如果 (@_ == 0) { print "arg1根本没有输入"; } ... - mob
@mob:我不确定这是唯一的参数。$arg1听起来好像还有更多。但我会添加它,并建议如果有更多要检查的参数,可以传递一个哈希表。 - Egga Hartung

1

个人而言,我喜欢使用undef来表示NULL - 这与DBI占位符/DBIx::Class/SQL::Abstract的做法相匹配。如果将其设置为字符串"NULL",则存在意外插入字符串而不是NULL本身的风险。

如果您正在使用Perl的最新版本(5.10或更高版本),那么请查看“defined-or”运算符////=,它们非常适用于处理参数。

关于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;
}

编辑(回答关于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 - 相同的原则 - 传入未定义的值以在数据库中创建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
});

我有一个关于您的陈述的问题,即使用undef来表示NULL与“DBI占位符/DBIx::Class/SQL::Abstract”所做的相匹配 - 您能详细说明一下吗?我不是完全确定您的意思。 - Stephen

1
唯一确定的方法是检查 @_ 的长度,以查看该槽位是否有参数。当存在必须参数时,这可能会被视为有些复杂,但其实并不需要。以下是许多对象访问器中使用的模式:
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');

0

map 是你的好朋友。试试这个:

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,然后再次使用//运算符。)


你的代码无法区分零和未定义值(http://ideone.com/MD6gN),因此它只是展示了与问题相同的问题。 - Rob Kennedy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接