如何取消引用返回值?

发布于 2024-12-09 07:31:54 字数 2294 浏览 1 评论 0原文

我不断遇到取消引用的问题,尤其是从函数返回值时。

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

我不断遇到类似以下错误:“期望偶数个参数得到引用” 或类似的错误。

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

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

Person对象

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对象 =>它实际上以哈希值的形式返回 引用
  • Func 返回哈希引用 =>它实际上返回为 标量
  • Func 返回数组对象=>它实际上返回一个数组 引用
  • 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]

加!那里有太多的文档,其中许多已经过时,因此很难准确地弄清楚在这些情况下正确或最好的做法是什么。

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

I keep running into problems with dereferencing, especially when returning values from functions.

The issue seems to be that whenever you return anything other than a scalar you are actually returning that object by reference - which is fine by me - but say when we pass these references to other functions and we need access to their guts again how do we do that correctly?

I keep running into errors like: "expecting even number of params got reference" or something to the effect.

Is there a general rule of thumb that I can use to simplify this whole process? I almost wish I didnt have to worry about dereferencing!

Here's an example of something I tried to do earlier today, and ran into all kinds of dereferencing problems that I spent a couple of hours trying to bash my way through -- so after reading, trying and failing, I'm here to ask you for the lowdown.

Person object

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

Various ways of making a Person object

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 returns hash object => it actually returns as a hash
    reference
  • Func returns hash reference => it actually returns as a
    scalar
  • Func returns array object => it actually returns an array
    reference
  • Func returns array reference => it actually returns a scalar
    ?
  • Func returns a scalar => it actually returns the value of the
    scalar?

Correct me if I've understood any of that wrong.

Then another issue for me is at the point of consuming the arguments of a function..

Depending on what I return back these bad boys are all going to behave differently!

    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]

Plus! There are way too many documents out there, many of them outdated, so it's hard to figure out exactly what the right or best thing to do is in these situations.

If someone has a magic chart that spells out when to use these different setups, and how to dereference given certain scenarios, blessed Hashes, Hash Ref, Scalar, etc etc. I would be eternally grateful as I've wasted hours trying to decipher this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

兲鉂ぱ嘚淚 2024-12-16 07:31:54

所有引用都是标量值

取消引用引用需要您知道引用的类型。可以使用 ref 功能。

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

取消引用不同的类型

  • 标量引用:$$scalar_ref

  • 数组引用: @$array_ref

  • 哈希引用:%$hash_ref

@_

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

参数和返回值

在 Perl 中,所有函数调用参数都被展平(折叠)为一个列表(从而丢失它们的标识),返回值也是如此。从函数的角度来看,它只能接受单个值列表,并且只能返回单个值列表。

示例场景:

函数的标量参数:

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]等等。

参考资料

All references are scalar values

De-referencing a reference requires that you know the type of the reference. The type of a reference can be found by using the ref function.

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

De-referencing different types

  • Scalar reference: $$scalar_ref

  • Array reference: @$array_ref

  • Hash reference: %$hash_ref

@_

@_ contains aliases of the parametres. Modifying @_ results in the modification of the original values. Always make a copy of the parametres as soon as possible and work on those copies; which you can safely modify without the original values changing.

Arguments and return values

In Perl, all function call arguments are flattened (collapsed) to a list (thus losing their identities) and so are the return values. From a function's perspective, it is able to accept only and a single list of values and can return only a single list of values.

Example scenarios:

Scalar argument to a function:

sub foo {
    my $arg = shift;
}

The same applies to all references; references are scalar values.

Array argument to a function:

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).

Hash argument to a function:

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.

Always pass references when multiple lists (arrays) or hashes are involved. This way, you can identify the lists individually.

$_ and @_ are different

Quoting your code (which uses $_ incorrectly):

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

$_ is called the default variable and is used in a number of situations where no explicit variable is stated. @_ on the other hand is only used for holding array parametres (aliases) inside a function. To refer to each element, we can use: $_[0], $_[1], et cetera.

References

  • You can read more about predefined variables in Perl from perldoc perlvar. I always use perldoc -v '$special_variable' from my terminal. If you use Windows, the single quotes have to be replaced with double quotes: perldoc -v "$special_variable".

  • Perl subroutines: perldoc perlsub

  • Perl references and nested data structures: perldoc perlref

盛装女皇 2024-12-16 07:31:54

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

从基础开始,Perl 中的标识符是一个匹配 [a-zA-Z_]\w+ 的字符串,排除了 unicode 的复杂性。该标识符可以是裸露的,也可以是带有符号前缀的。

最重要且经常被忽视的标志是 glob,*,它是所有变量的容器(至少在 my 不得不毁掉派对并与众不同,隐藏在垫子中)。

当您有一个名为 foo 的标识符时,*foo glob 包含 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 符号并直接使用其他符号。不过,您可以保留括号,${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);

在您使用引用的代码中应用这些更改,事情应该会再次变得有意义。

You are missing some crucial aspects of working with references.

Starting from the basics, an identifier in Perl is a string matching [a-zA-Z_]\w+ excluding the complexities of unicode. This identifier can be bare, or prefixed with a sigil.

The most important and oft overlooked sigil is the glob, *, which is the container of all things variable (at least until my had to ruin the party and be different, hiding in a pad).

When you have an identifier named foo, then the *foo glob contains all that foo could be:

$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

There are other types as well, but they are less common.

In each of the examples above, the sigil is placed in front of the identifier, and dereferences the reference values stored in the glob *foo. The full syntax of this is a bit unwieldy @{*foo{ARRAY}}, so perl lets you omit the sad overlooked glob sigil and use the others directly. You can keep the brackets though, ${foo} or @{foo} or any other sigil, which mean the same thing as $foo and @foo respectively.

It turns out, that you don't have to place a bareword identifier behind the sigil or inside the brackets, but any singular value.

my $scalar_foo_ref = \$foo;

say $scalar_foo_ref;   # prints $foo
say ${$scalar_foo_ref}; # same

The sigil always expects to dereference a value of its type (or one that can pretend to be), and will throw an error otherwise.

So assuming the following code:

my @array = qw(a b c);

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

You can transform it to using references easily. First you would change the declaration to use references, which are scalars, so stored in a $foo:

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

And then in the rest of the code, substitute the string array with the name of the reference, $array:

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

That's it, all there is to references. So, finally to your code. Take the following lines:

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

In the first, you properly use the {...} construct to create an anonymous hash reference. You could have also written it in the horribly verbose form as:

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

But don't do that.

The next line is where the problems happen, and where you need to follow the substitution rules from above. You probably saw code that looks like this:

%options = (%options, %defaults);

And when you changed the sigils, everything went wrong. What perl actually does when it sees:

$options = ($options, $defaults);

Is it executes and then throws away everything but the last element of the list (in this case ($options, and then assigns the last element to the scalar on the lhs of the =, making your line equivalent to:

$options = $defaults;

Which of course is not what you wanted. Instead, substitute your hashref names in for the bareword names:

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

This happens to probably be merging in the wrong order, with your defaults overriding the options. To fix that, just reverse the two:

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

Apply these changes across your code where you are using references. Things should start to make sense again.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文