如何检查@_的元素在Perl子文件中是否是只读的?

yhived7q  于 2023-05-29  发布在  Perl
关注(0)|答案(2)|浏览(133)

当我的sub需要通过其中一个参数传回一些输出时(返回值已经用于传回另一个输出),当该参数保存来自调用者的常量时,会发生运行时错误。示例:

# Increases the input by 1 and reports if the new value is bigger than 10.
sub IncreaseByOne {
    $_[0] ++;
    return ($_[0] > 10 ? 1 : 0);
}

my $x = 1;
IncreaseByOne($x);  # This is OK.
IncreaseByOne(1);   # This is not OK.

我的问题是:在sub中是否有一种方法来判断$_[0]是否是只读的?(我知道很多人会说通过一个参数返回信息不是一个好的做法。但我真的需要在我的情况下)。

ncgqoxb0

ncgqoxb01#

如果sub应该增加值,那么如果不可能,它应该失败:不要因为默默地忽略了一个问题而让用户感到惊讶。这个错误是一件好事!
但如果真的有必要,可以测试标量是否为只读:

use Scalar::Util 'readonly';

sub IncreaseByOne {
  readonly($_[0]) or $_[0]++;
  ...
}

请注意,与其使用out-parameters(这会导致像您遇到的意外问题),通常更好的方法是返回多个值的列表:

sub IncreaseByOne {
  my ($x) = @_;
  $x++;
  my $more_than_ten = ($x > 10);
  return $x, $more_than_ten;
}
p1tboqfb

p1tboqfb2#

使用Scalar::Util::readonly的解决方案并不能完全解决这个问题。我有以下用例:

sub Lookup {
  # Get a value from a table, file, or database, using the label in $_[0].
  # Set $_[1] to that value if that parameter is writable.
  # Return an error code or a success code.
  my $value = get_value_from_somewhere($_[0]);
  my $error = get_error_from_last_operation();
  $_[1] = $value if not readonly($_[1]);
  return $error;
}

sub main {
  my $error = Lookup("Foo",undef); # Fails
  process_error($error) if $error < 0;
  
  my $error = Lookup("Foo"); # Fails
  process_error($error) if $error < 0;
  
  my $dummyvar = "";
  my $error = Lookup("Foo", $dummyvar); # Succeeds
  process_error($error) if $error < 0;
}

如果开发人员只需要检查返回的错误代码,他/她可能会使用“undef”作为第二个参数,或者忽略该参数,而不是创建一个虚拟变量,只是为了避免Perl错误“Modification of a read-only value attempted at line .”。
另一方面,如果尝试以下操作,则它也不会具有所需的行为:

sub Lookup2 {
  # Get a value from a table, file, or database, using the label in $_[0].
  # Set $_[1] to that value if that parameter is writable.
  # Return an error code or a success code.
  my $value = get_value_from_somewhere($_[0]);
  my $error = get_error_from_last_operation();
  $_[1] = $value if defined $_[1] and not readonly($_[1]);
  return $error;
}

sub main2 {
  my $error = Lookup2("Foo",undef); # Succeeds
  process_error($error) if $error < 0;
  
  my $error = Lookup2("Foo"); # Succeeds
  process_error($error) if $error < 0;
  
  my $result;
  my $error = Lookup2("Foo", $result); # Fails
  process_error($error) if $error < 0;
}

在第二个版本中,尝试将值放入$result失败,因为在调用Lookup 2时该变量具有未定义的值。
是否有一个Lookup例程的解决方案,可以允许以下调用按预期工作?

my $x; $error = Lookup("Foo", $x);       # Sets $x with a value.
my $y = ""; $error = Lookup("Foo", $y);  # Sets $y with a value.
$error = Lookup("Foo");                  # Just returns $error.
$error = Lookup("Foo", undef);           # Just returns $error.

当然,第二个参数可能总是需要一个标量变量的引用,但最好支持第二个参数的非引用语法(即通过设置$_[1])。

相关问题