【问题标题】:Passing only some subroutine arguments by reference in Perl在 Perl 中通过引用仅传递一些子例程参数
【发布时间】:2017-06-18 23:00:40
【问题描述】:

我正在编写一个带有多个参数的子例程。这些参数中的大多数都是标准的按值传递排序,在子例程中对它们所做的更改在子例程之外无关紧要。但是其中一个是一个对象(祝福引用),如果它被传入,我想对其进行更改,它在子例程之外可用。如果它没有传入,我想实例化它并同样对待它就好像它被传入(但最终返回)一样。

例如:


my $foo = Private::Foo->new();

# $foo->{'something'} eq 'old value'

Private::Foo->do_things('abc', 'xyz', $foo);

# $foo->{'something'} eq 'new value'

my $foo2 = Private::Foo->do_things('def');

# $foo2->{'something'} eq 'old value'

package Private::Foo;

# ...

sub do_things {
    my ($self, $arg1, $arg2, $foo) = @_;

    unless (defined $foo) {
         $foo = Private::Foo->new();
    }

    if ($arg1 eq 'abc') {
        $foo->{'something'} = 'new value';

        return;
    }

    return $foo;
}

我希望使用尽可能简洁的语法来执行此操作,并且可以使用 Perl v5.22 及更高版本中提供的任何功能。 (我试图弄清楚如何使用refaliasing 做到这一点,但它不是很干净。)

我错过了什么?

【问题讨论】:

  • 关于“我错过了什么?”,什么都没有。您的代码按原样运行。
  • Re "大部分参数都是标准的按值传递排序",Perl 总是通过引用传递。例如,perl -e'sub f { $_[0] = "def"; } my $x = "abc"; f($x); CORE::say $x;' 输出 def
  • 呸,你说得对,这似乎确实有效。我之前尝试的一些事情组合给了我错误;我猜他们是无关的。另外,我所说的“按值传递”是标准my ($foo) = @_,在我的示例(而不是您的示例)中,它立即出现在子例程的开头。
  • 在收到参数后复制参数并不会改变它们通过引用传递的事实。你复制了一个论点这一事实并不能阻止你改变论点。

标签: perl pass-by-reference


【解决方案1】:

首先,子程序参数总是通过引用传递。

$ perl -e'sub f { $_[0] = "def"; }   my $x = "abc"; f($x); CORE::say $x;'
def

更重要的是,您的代码完全符合您的要求。

$ perl -e'
    {
        package Private::Foo;

        sub new { my $class = shift; bless({ something => "old_value" }, $class) }

        sub do_things {
            my ($self, $arg1, $arg2, $foo) = @_;

            unless (defined $foo) {
                 $foo = Private::Foo->new();
            }

            if ($arg1 eq "abc") {
                $foo->{something} = "new value";
                return;
            }

            return $foo;
        }
    }

    use feature qw( say );

    my $foo = Private::Foo->new();
    Private::Foo->do_things("abc", "xyz", $foo);
    say $foo->{something};

    my $foo2 = Private::Foo->do_things("def");
    say $foo2->{something};
'
new value
old_value

也就是说,你可以清理一下你的方法:

sub do_things {
    my ($class, $arg1, $arg2, $foo) = @_;

    $foo //= $class->new();

    if ($arg1 eq 'abc') {
        $foo->{something} = 'new value';
    }

    return $foo;
}

如果你清理你的调用约定会更好。

Private::Foo->do_something($arg1, $arg2, $foo);

my $foo2 = Private::Foo->do_something($arg1, $arg2);

意义远不如

$foo->do_something($arg1, $arg2);

( my $foo2 = Private::Foo->new )->do_something($arg1, $arg2);

【讨论】:

  • 无论$arg1 的值如何,清理后的方法都会返回$foo,这与我的示例不同。另外,如果$foo 未定义,我还没有说明我还有其他代码要运行,所以//= 在这里对我没有多大帮助。
  • Re "不管$arg1的值如何,你清理的方法都会返回$foo,这和我的例子不同。",那又怎样?我从来没有说过是一样的。我说它更干净。
  • Re "我忽略了如果 $foo 未定义,我还有其他代码要运行",您可以通过检查 @_ 的大小来检查提供的参数数量.也就是说,在你提出这个问题之前,你的调用约定已经过于复杂了。将缺少的参数与未定义的参数区分开来太过分了。
  • 添加了更简单的调用约定来回答。
猜你喜欢
  • 1970-01-01
  • 2021-03-09
  • 2013-05-22
  • 1970-01-01
  • 2020-03-09
  • 2014-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多