【问题标题】:How do you dereference return values?你如何取消引用返回值?
【发布时间】:2011-12-06 14:29:33
【问题描述】:

我一直遇到解除引用的问题,尤其是在从函数返回值时。

问题似乎是,每当您返回标量以外的任何内容时,您实际上都是通过引用返回该对象 - 这对我来说很好 - 但是当我们将这些引用传递给其他函数时,我们需要再次访问它们的胆量我们如何正确地做到这一点?

我不断遇到错误,例如:“期望偶数个参数得到引用” 或类似的东西。

我可以使用一个通用的经验法则来简化整个过程吗?我几乎希望我不必担心取消引用!

这是我今天早些时候尝试做的一个示例,但遇到了各种取消引用问题,我花了几个小时试图解决这个问题 - 所以在阅读、尝试和失败之后,我就在这里向你询问内幕。

人物对象

Person
 has name [Str]
 has madeby [Str]
 has height [Num]
 has id [Num]

制作 Person 对象的各种方法

sub person_maker{
 my($this,%options) = @_;
 my $person = Person->new(%options);
   return $person;
}

sub make_person_named_james{
 my($this,$options) = @_;
 my $default = { name => 'James', madeby => 'name' };
   $options = ($options,$defaults); #merge default and options hash
   return($this->person_maker($options));
}

sub make_person_from_id{
 my($this,$id) = @_;
 my $default = { name => 'nameless person', madeby => 'id' };
   $default = ($default,{ id => $id });
   return($this->person_maker($default);
}

sub make_person_from_person{
 my($this,$person) = @_;
 my $person_options = {
   name => $person->name().'_twin',
   height => $person->height(),
   id => $person->id() + 1,
   madeby => 'person'
 };
 return($this->person_make($person_options));
}
  • Func 返回 hash 对象 => 它实际上作为 hash 返回 参考
  • Func 返回 hash 引用 => 它实际上返回一个 标量
  • Func 返回 array 对象 => 它实际上返回一个 array 参考
  • Func 返回 数组引用 => 它实际上返回一个 标量 ?
  • Func 返回一个 标量 => 它实际上返回了 标量?

如果我理解错了,请纠正我。

那么对我来说另一个问题是使用函数的参数..

根据我返回的内容,这些坏男孩的行为都会有所不同!

    sub myfunc{
      my($self,$args) = @_ # list of arguments (what if the args are mixed?)
  OR
      my($self,$args) = $_ # scalar (this wont work here $args will by undef
  OR
      my $self = shift; # pop the first arg
      my $args = shift; # pop the second arg
  OR
      my $self = $_[0] 
      my $args = $_[1]

另外!那里有太多的文档,其中许多已经过时,因此很难确切地确定在这些情况下应该做什么或最好的事情。

如果有人有一张神奇的图表,说明何时使用这些不同的设置,以及如何在某些场景下取消引用,祝福哈希、哈希引用、标量等。我会永远感激,因为我已经浪费了几个小时来尝试来破译这个。

【问题讨论】:

    标签: perl oop return-value dereference


    【解决方案1】:

    所有引用都是标量值

    取消引用引用要求您知道引用的类型。可以使用ref 函数找到引用的类型。

    my $array_ref = [];
    print ref $array_ref;  # prints: ARRAY
    

    取消引用不同的类型

    • 标量参考:$$scalar_ref

    • 数组引用:@$array_ref

    • 哈希参考:%$hash_ref

    @_

    @_ 包含参数的别名。修改@_ 会导致修改原始值。始终尽快制作参数副本并处理这些副本;您可以安全地对其进行修改,而无需更改原始值。

    参数和返回值

    在 Perl 中,所有函数调用参数都被展平(折叠)到一个列表中(因此失去了它们的身份),返回值也是如此。从函数的角度来看,它只能接受一个 single 值列表,并且只能返回一个 single 值列表。

    示例场景:

    函数的标量参数:

    sub foo {
        my $arg = shift;
    }
    

    这同样适用于所有引用;引用是标量值。

    函数的数组参数:

    sub foo {
        my @args = @_;
    }
    
    foo( 'bar', 'baz' );
    foo( ( 'bar', 'baz' ), ( 'qux', 'spam' ) );  # In no way can the foo subroutine identify that the original arguments were two lists (same with arrays).
    

    函数的散列参数:

    sub foo {
        my %arg = @_;
    }
    
    foo( 'bar' => 'baz' );
    foo( ( 'bar' => 'baz' ), ( 'qux' => 'spam' ) );  # In no way can the foo subroutine identify that the original arguments were two hashes.
    

    当涉及多个列表(数组)或哈希时,始终传递引用。这样,您可以单独识别列表。

    $_@_ 不同

    引用您的代码(错误地使用了$_):

    my($self,$args) = $_ # scalar (this wont work here $args will by undef
    

    $_ 被称为默认变量,在许多没有明确声明变量的情况下使用。另一方面,@_ 仅用于在函数内保存数组参数(别名)。要引用每个元素,我们可以使用:$_[0]$_[1] 等。

    参考文献

    • 您可以从perldoc perlvar 阅读更多关于 Perl 中预定义变量的信息。我总是在终端上使用perldoc -v '$special_variable'。如果您使用 Windows,则必须将单引号替换为双引号:perldoc -v "$special_variable"

    • Perl 子例程:perldoc perlsub

    • Perl 引用和嵌套数据结构:perldoc perlref

    【讨论】:

    • 我对接受的答案投了反对票,因为它没有直接回应所提出的问题。 OP 想要取消引用函数的返回值。然而,公认的答案是关于函数参数。与返回值完全相反!也许,在响应 migt 的底部,您可以找到此查询的答案,但此答案绝对不在轨道上。 OP 清楚地知道参考是什么,以及如何使用它们。但即使是我,多年后,仍然没有找到“我的@rv = @$functionName_asRef($param1, $param2)”的方法;
    【解决方案2】:

    您遗漏了使用参考文献的一些关键方面。

    从基础开始,Perl 中的标识符是一个匹配 [a-zA-Z_]\w+ 的字符串,不包括 unicode 的复杂性。这个标识符可以是裸露的,也可以是带有印记的前缀。

    最重要且经常被忽视的印记是 glob,*,它是所有变量的容器(至少在 my 不得不毁掉聚会并变得与众不同之前,隐藏在一个垫子里)。

    当您有一个名为 foo 的标识符时,*foo 全局包含所有 foo 可能是:

    $foo as a scalar, a singular value
    @foo as an array, a plural value
    %foo as a hash, a plural value
    &foo as code, singular if a reference \&foo, could be plural if making a call
    

    还有其他类型,但不太常见。

    在上面的每个示例中,印记位于标识符前面,并取消引用存储在 glob *foo 中的引用值。这个完整的语法有点笨拙@{*foo{ARRAY}},所以 perl 让你可以省略悲伤的被忽视的 glob sigil 并直接使用其他的。不过,您可以保留括号,${foo}@{foo} 或任何其他符号,它们分别与 $foo@foo 表示相同的含义。

    事实证明,您不必在印记后面或括号内放置裸字标识符,而是任何奇异值。

    my $scalar_foo_ref = \$foo;
    
    say $$scalar_foo_ref;   # prints $foo
    say ${$scalar_foo_ref}; # same
    

    印记总是希望取消引用其类型的值(或可以假装的值),否则会抛出错误。

    所以假设如下代码:

    my @array = qw(a b c);
    
    say $array[0];  # a
    say join ', ' => @array;  # a, b, c
    

    您可以轻松地将其转换为使用引用。首先,您将声明更改为使用引用,它们是标量,因此存储在 $foo 中:

    my $array = [qw(a b c)]; # square brackets make an array ref
    # or
    my $array = \@array_of_hopefully_another_name; # \ does too
    

    然后在其余代码中,将字符串array 替换为引用名称$array

    say $$array[0];  # a
    say join ', ' => @$array;  # a, b, c
    

    就是这样,所有的参考。所以,最后到你的代码。采取以下几行:

    my $default = { name => 'James', madeby => 'name' };
    $options = ($options,$defaults); #merge default and options hash
    

    首先,您正确使用{...} 构造来创建匿名哈希引用。你也可以把它写成非常冗长的形式:

    my $default = do {my %hash = (name => 'James', madeby => 'name'); \%hash};
    

    但不要那样做。

    下一行是问题发生的地方,你需要遵循上面的替换规则。您可能看到过如下代码:

    %options = (%options, %defaults);
    

    当你改变了印记时,一切都出错了。 perl 在看到时实际上做了什么:

    $options = ($options, $defaults);
    

    它是否执行然后丢弃除了列表的最后一个元素之外的所有元素(在本例中为($options,,然后将最后一个元素分配给= 的 lhs 上的标量,使您的行相当于:

    $options = $defaults;
    

    这当然不是你想要的。相反,将您的 hashref 名称替换为裸词名称:

    %$options = (%$options, %$defaults);
    

    这恰好可能以错误的顺序合并,您的默认设置会覆盖选项。要解决这个问题,只需颠倒两者:

    %$options = (%$defaults, %$options);
    

    在您使用引用的代码中应用这些更改。事情应该又开始变得有意义了。

    【讨论】:

      猜你喜欢
      • 2012-10-04
      • 2016-08-27
      • 2017-06-18
      • 2016-12-05
      • 1970-01-01
      • 1970-01-01
      • 2019-03-22
      • 2021-12-09
      • 1970-01-01
      相关资源
      最近更新 更多