【问题标题】:How to have sub use $_ when parameter omitted?省略参数时如何让子使用 $_?
【发布时间】:2020-07-29 03:35:04
【问题描述】:

当参数被省略时,如何让 perl sub 使用$_,就像chr 一样?这是最好的方法吗?

my @chars = map { chr } @numbers;  # example

my @trimmed_names = map { trim } @names;

sub trim
{
    my $str = shift || $_;
    $str =~ s/^\s+|\s+$//g;
    return $str;
}

【问题讨论】:

  • 够用了my $str = shift;
  • @PolarBear 不是它不是
  • 根据 url perlmaven.com/how-to-set-default-values-in-perl 设置默认值的常用方法是使用 // sub f { my ($count, $other_param) = @_; $count //= $SOME_DEFAULT; }
  • 简而言之:(1)一个合格的“是”我会说(2)子声明必须在它使用之前出现,如果一个人要调用不带括号的(3)||会删除0''(空字符串)这两个可能的参数,所以使用//(定义或)。更多详情见答案
  • @CJ7 你可以使用 Params::Validate 包metacpan.org/pod/Params::Validate (只是我的观点,因为你提到你想用作char(作为内置函数))示例sub foo { my @str = validate_pos( @_, { default => $_ } ); print "\n @str \n"; } foo("Batman"); my @a = (1..9); foreach(@a){ foo; }

标签: perl parameters subroutine


【解决方案1】:

$_ 直接在其作用域内调用的 sub 中看到,因此您确实可以直接使用它

sub trim { s/^\s+|\s+$//gr }   # NOTE: doesn't change $_

/r modifier 会返回更改后的字符串,原件没有更改,这很重要。

但是,这可能很棘手,并且可能(很容易)导致微妙且非常难以发现的错误。这是一个现成的例子。如果我们在处理过程中更改了sub中的$_,比如

sub trim {           # WARNING: caller's data changed
    s/^\s+|\s+$//g;
    return $_;
}

那么调用者中@names 的元素发生了变化,这通常是没有预料到的。这是因为更改后的上范围 $_ 本身在 map 的主体中具有别名。 因为$_ 是许多事情的方便默认值,我们必须跟踪所有事情在子中使用。所以我确实会先复制$_,或者更安全的localize 它,在sub 中并使用它。

最后,为了使用传递的参数或$_(在调用点)

sub trim {
    my $str = @_ ? shift : $_;   #/
    $str =~ s/^\s+|\s$//gr;
}

my @trimmed_names = map { trim } @names;  # may omit () if sub declared before

这是因为$_ 的可见性与@_ 中的参数列表无关,因此也可以传递参数。在这里,我们还可以(更)安全地复制$_

问题中的shift || $_ 将忽略@_ 中的0''(空字符串),原则上有效输入是什么; shift // $_ 将关闭 undef,这也是一个可能的输入。感谢ikegami对此的评论。因此显式测试@_中是否有任何内容。

虽然传递 undef 的变量在此处无效,但通常它可能是有效的输入。更重要的是,这里的前提是使用参数(如果提供),所以我们应该这样做,然后(希望)从调用代码中检测到错误(如果传递 undef 不应该发生),而不是悄悄地-通过切换到$_来踩它。

所以,我的回答是合格的“是”——这是一种方法;但我可能会发现使用用户的潜艇混合范围的代码库很不舒服。 map 中的这个例子 trim 就目前而言是完全安全的,但是这样的函数还能用在哪里呢?为什么不只是传递参数?

注意:为了能够在不带括号的情况下调用用户定义的子程序,我们必须在源代码中调用点之前声明它,以便解释器知道那个裸词 (trim) 是什么,因为没有括号就没有任何提示。


我认为此时值得回想一下,子参数是别名的,而不是复制的,所以如果@_ 的元素本身发生了变化,那么调用者的数据也会发生变化。这与$_ 没有直接关系,但行为可以。

【讨论】:

  • 如果将参数传递给子会发生什么?我希望它与 chrprint 之类的函数相同。
  • @CJ7 添加到答案中如何使用传递的参数或$_
【解决方案2】:

您可以使用_ 原型。

sub trim(_) { $_[0] =~ s/^\s+|\s+\z//rg }

否则,如果没有提供参数,您可以简单地使用 $_

sub trim { ( @_ ? $_[0] : $_ ) =~ s/^\s+|\s+\z//rg }

不管怎样,

say for map trim, @strings;
  -or-
say for map trim($_), @strings;

【讨论】:

  • 请注意,如果您不小心传递了一个未定义的值,使用$_[0] // $_ 可能会导致奇怪的结果,因此使用@_ ? $_[0] : $_
猜你喜欢
  • 2013-06-27
  • 2016-09-20
  • 2011-03-09
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 2021-07-09
相关资源
最近更新 更多