我一直遇到解除引用问题,尤其是在从函数返回值时。
问题似乎是每当你返回标量以外的任何东西时,你实际上都是通过引用返回那个对象 - 这对我很好 - 但是说当我们将这些引用传递给其他函数时我们需要再次访问它们的内容我们如何正确地做到这一点?
我一直遇到如下错误:“期待偶数段参数得到参考”或其他类似的效果。
是否有一般的经验法则可以用来简化整个过程?我几乎希望我不必担心解除引用!
这是我今天早些时候尝试做的事情的一个例子,并遇到了各种各样的解除引用问题,我花了几个小时试图抨击我的方式 - 所以在阅读,尝试和失败后,我在这里问你的下降。
人物对象
Person
has name [Str]
has madeby [Str]
has height [Num]
has id [Num]
制作人物对象的各种方法
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));
}
如果我理解了任何错误,请纠正我。
然后我的另一个问题就是消耗函数的参数..
根据我的回报,这些坏男孩的行为都会有所不同!
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]
加!有太多的文件,其中许多已经过时,所以在这些情况下很难弄清楚正确或最好的事情是做什么的。
如果某人有一个神奇的图表,说明何时使用这些不同的设置,以及如何在某些情况下取消引用,祝福哈希,哈希参考,标量等等。我会永远感激,因为我浪费了几个小时的尝试解读这个。
答案 0 :(得分:10)
取消引用引用要求您知道引用的类型。可以使用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]
等等。
您可以从perldoc perlvar
中了解有关Perl中预定义变量的更多信息。我总是在终端上使用perldoc -v '$special_variable'
。如果您使用Windows,则单引号必须替换为双引号:perldoc -v "$special_variable"
。
Perl子例程:perldoc perlsub
Perl引用和嵌套数据结构:perldoc perlref
答案 1 :(得分:4)
您缺少使用参考文献的一些重要方面。
从基础开始,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
还有其他类型,但它们不常见。
在上面的每个示例中,sigil都放在标识符的前面,并取消引用存储在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
sigil总是期望取消引用其类型的值(或者可以假装的类型),否则将引发错误。
假设以下代码:
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);
在您使用引用的代码中应用这些更改。事情应该开始变得有意义了。