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

时间:2012-01-16 08:52:18

标签: perl map

在(非常感谢)perlmonks网站上,我发现following snippet that trims字符串两边的空格:

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

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

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

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

或者是一个简单的修剪,当需要修剪数组时,这样调用:

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

4 个答案:

答案 0 :(得分:11)

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

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

其次,考虑上面的例子并将其与:

进行比较
#e.2.
sub trim
{
    for (@_) { s/^\s+//, s/\s+$// };
}

有什么区别?

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

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

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

如何?

通过检查是否完全定义了wantarray。只要函数位于右侧并且返回值分配给变量“defined wantarray”为真(无论标量/数组上下文如何)。

答案 1 :(得分: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]
}

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

该功能可以简化一点:

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

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

    wantarray ? @_ : shift
}

前两行可以合并为一行,因为它们只是使用不同的源值做同样的事情(分配给@_)。并且最后不需要进行外部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

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

my @result = map trim, @test_array;

或更详细地说:

my @result = map trim($_), @test_array;

它可以在类似于chomp

的while循环中使用
while (<$file_handle>) {
    trim; 
    # do something
}

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

答案 2 :(得分:5)

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

man perlfunc 
chomp VARIABLE 
chomp( LIST ) 
chomp 
     

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

答案 3 :(得分:1)

请注意,这正是Text::Trim的实施方式。请参阅有关各种用例的说明。使用wantarray的游戏允许区分各种上下文并为每个上下文实现不同的语义。

我个人更喜欢单一语义,因为它更容易理解和使用。我会避免使用$_默认变量或修改,与Nylon Smile的示例1一致。