在Perl中,一个函数应该执行wantarray舞蹈,还是我们可以期望调用者使用map?

Kon*_*rak 8 perl map

在(非常感谢)perlmonks网站上,我发现以下代码片段修剪了字符串两边的空格:

sub trim {
  @_ = $_ if not @_ and defined wantarray;
  @_ = @_ if defined wantarray;
  for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// }
  return wantarray ? @_ : $_[0] if defined wantarray;
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么作者几乎每一行都要检查各种各样的问题.为什么不修剪字符串,让程序员map在传递数组时使用?

这个修剪之间的区别是什么,如下所示:

my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 ');
my @result = trim(@test_array);
Run Code Online (Sandbox Code Playgroud)

或者一个简单的修剪,当需要修剪数组时称为这样:

my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 ');
my @result = map { trim($_) } @test_array;
Run Code Online (Sandbox Code Playgroud)

Nyl*_*ile 11

首先,如果你抽象出那张地图会更好:

#e.1.
sub trim
{
    my @ar = @_;
    for (@ar) { s/^\s+//, s/\s+$// };
    return wantarray ? @ar : $ar[0];
}
Run Code Online (Sandbox Code Playgroud)

其次,考虑以上示例并将其与以下内容进行比较:

#e.2.
sub trim
{
    for (@_) { s/^\s+//, s/\s+$// };
}
Run Code Online (Sandbox Code Playgroud)

有什么不同?

E.1.返回一个新的修剪数组,而e.2.修改原始数组.

好了,原来神秘的子程序做了什么?

它自动神奇(是啊,它的Perl)修改原始数组,如果你没有将返回值赋值给任何东西或保持原始数组不变,并返回一个新的修剪数组,如果你将返回值赋给另一个变量.

怎么样?

通过检查是否完全定义了wantarray.只要函数在右侧并且返回值被赋值给变量"defined wantarray"为真(无论标量/数组上下文如何).

  • 如果没有参数传递给函数,作者还希望函数隐式地对`$ _`进行操作. (2认同)

Eri*_*rom 7

逐行打破这一点,因为它还没有:

sub trim {
  @_ = $_ if not @_ and defined wantarray;
     # if there are no arguments, but a return value is requested
     # then place a copy of $_ into @_ to work on

  @_ = @_ if defined wantarray;
     # if the caller expects a return value, copy the values in @_ into itself 
     # (this breaks the aliasing to the caller's variables)

  for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// }
     # perform the substitution, in place, on either @_ or $_ depending on
     # if arguments were passed to the function

  return wantarray ? @_ : $_[0] if defined wantarray;
     # if called in list context, return @_, otherwise $_[0]
}
Run Code Online (Sandbox Code Playgroud)

我同意代码对所有wantarray检查都有点乏味,但结果是一个与Perl的内置函数具有一定灵活性的函数.使函数"智能"的最终结果是清理调用站点(避免循环结构,临时变量,重复......),这取决于使用函数的频率,可以有意义地提高代码可读性.

该功能可以简化一点:

sub trim {
    @_ = @_ ? @_ : $_ if defined wantarray;

    s/^\s+//, s/\s+$// for @_ ? @_ : $_;

    wantarray ? @_ : shift
}
Run Code Online (Sandbox Code Playgroud)

前两行可以合并为一行,因为它们@_只使用不同的源值执行相同的操作(分配).并且最后不需要外部return ... if defined wantarray检查,因为在void上下文中返回值无论如何都不会做任何事情.

但我可能会改变最后一行,wantarray ? @_ : pop因为它使它表现得像一个列表(标量上下文中的最后一个元素).

一旦完成所有操作,就可以使用以下调用样式:

my @test_array = ( 'string1', ' string2', 'string3 ', ' string4 ');

my @result = trim @test_array;
my $result = trim $test_array[0];
trim @test_array; # in place trim
Run Code Online (Sandbox Code Playgroud)

甚至还支持呼叫站点循环:

my @result = map trim, @test_array;
Run Code Online (Sandbox Code Playgroud)

或者更详细地说:

my @result = map trim($_), @test_array;
Run Code Online (Sandbox Code Playgroud)

它可以在类似的while循环中使用 chomp

while (<$file_handle>) {
    trim; 
    # do something
}
Run Code Online (Sandbox Code Playgroud)

关于Perl中dwimmery的观点喜忧参半.我个人喜欢它,当函数给我灵活地以一种有意义的方式对调用者进行编码时,而不是解决函数的刚性接口.


kub*_*zyk 5

可能作者想要模仿标准chomp函数的行为.您无需在自己的功能中执行此操作.

man perlfunc 
chomp VARIABLE 
chomp( LIST ) 
chomp 
Run Code Online (Sandbox Code Playgroud)

[...]如果你选择一个列表,每个元素都会被选中.[...]