【问题标题】:How can I distinguish between an argument that was not passed and one that was passed with a false value?如何区分未传递的参数和传递的带有错误值的参数?
【发布时间】:2012-01-08 03:18:04
【问题描述】:

我试图找出在 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')";
  }
}

【问题讨论】:

    标签: perl arguments undefined shift subroutine


    【解决方案1】:

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

    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,因为它改变了@_,它将要求我们将 @_ 的原始大小存储在某处。)

    【讨论】:

    • 谢谢,这很好,很彻底。我没有考虑过接收 undef 值和不接收值之间的区别(与我的情况无关,但值得考虑)。
    【解决方案2】:

    怎么样:

    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

    【讨论】:

    • elsif (@_ == 0) { print "arg1 did not come in at all"; } ... ?
    • @mob:我不确定这是否是唯一的参数。$arg1 听起来还有更多。但是如果有更多参数需要检查,我会添加它并建议传递一个哈希......
    【解决方案3】:

    我个人喜欢保留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;
    }
    

    编辑(回答关于 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
    });
    

    【讨论】:

    • 我对您的陈述有疑问,即使用 undef 表示 NULL“与 DBI 占位符/DBIx::Class/SQL::Abstract all 的作用相匹配”——您能详细说明这一点吗?我不完全确定你的意思。
    【解决方案4】:

    唯一确定的方法是检查@_ 的长度以查看该槽中是否存在参数。当还有强制性参数时,这可以被视为有点复杂,但并非必须如此。这是许多对象访问器中使用的模式:

    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');
    

    【讨论】:

      【解决方案5】:

      地图是你的朋友。试试这个:

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

      【讨论】:

      • 您的代码无法区分零和 undef (ideone.com/MD6gN),因此它实际上只是演示了问题已经解决的相同问题。
      猜你喜欢
      • 2022-11-14
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 2014-12-18
      • 2014-04-27
      • 2012-05-22
      • 1970-01-01
      • 2015-07-20
      相关资源
      最近更新 更多