开发者

How do you dereference return values?

开发者 https://www.devze.com 2023-04-12 11:41 出处:网络
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 returni

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.


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消