子例程是否应该始终 return 显式?

Should a subroutine always return explicitly?

如果 perlcritic 说 "having no returns in a sub is wrong",如果真的不需要它们,有什么替代方案?

我养成了两个明显的坏习惯:

例如,我可能会..

#!/usr/bin/perl
use strict;
use warnings;

@main::array = (1,4,2,6,1,8,5,5,2);

&sort_array;
&push_array;
&pop_array;

sub sort_array{
    @main::array = sort @main::array;
    for (@main::array){
        print "$_\n";
    }
}

sub push_array{
    for ( 1 .. 9 ){
        push @main::array, $_;
    }
}

sub pop_array {
    for ( 1 .. 3 ){
        pop @main::array;
    }
}

我不经常这样做。但在上面,这是有道理的,因为我可以隔离操作,不必担心来回传递值,而且它对我来说通常看起来很整洁。

但正如我所说,perl 评论家说它错了 - 因为没有 return..

那么,是否有人能够解释我正在尝试做的事情并提出一种更好的方法来处理这种 perl 编码风格?例如。我是在做 OOP 吗?

简而言之 - 是的,您基本上是在进行 OO,但在某种程度上会让所有人感到困惑。

像那样做潜艇的危险在于你是在远距离行动。这是一种糟糕的编码风格,必须完全在其他地方寻找可能会破坏您的代码的内容。

这通常就是为什么要尽可能避免 'globals' 的原因。

对于一个简短的脚本,太多并不重要。

关于 return 值 - Perl return 默认为最后一个表达式的结果。 (参见:return

(In the absence of an explicit return, a subroutine, eval, or do FILE automatically returns the value of the last expression evaluated.)

Perl critic 标记的原因是:

Require all subroutines to terminate explicitly with one of the following: return, carp, croak, die, exec, exit, goto, or throw.

Subroutines without explicit return statements at their ends can be confusing. It can be challenging to deduce what the return value will be.

Furthermore, if the programmer did not mean for there to be a significant return value, and omits a return statement, some of the subroutine's inner data can leak to the outside.

Perlcritic 并不总是正确的 - 如果有充分的理由做你正在做的事情,然后将其关闭。只要您考虑过并意识到风险和后果。

我个人认为明确 return 是更好的风格,即使只是 return;.

无论如何,以(粗略的)面向对象的方式重新起草您的代码:

#!/usr/bin/perl
use strict;
use warnings;

package MyArray;

my $default_array = [ 1,4,2,6,1,8,5,5,2 ];

sub new {
   my ( $class ) = @_;
   my $self = {};
   $self -> {myarray} = $default_array;
   bless ( $self, $class );
   return $self;
}

sub get_array { 
   my ( $self ) = @_;
   return ( $self -> {myarray} ); 
}

sub sort_array{
    my ( $self ) = @_;
    @{ $self -> {myarray} } = sort ( @{ $self -> {myarray} } );
    
    for ( @{ $self -> {myarray} } ) {
        print $_,"\n";
    }
    return 1;
}

sub push_array{
    my ( $self ) = @_;
    for ( 1 .. 9 ){
        push @{$self -> {myarray}}, $_;
    }
    return 1;
}

sub pop_array {
    my ( $self ) = @_;
    for ( 1 .. 3 ){
        pop @{$self -> {myarray}};
    }
    return 1;
}

1;

然后调用它:

#!/usr/bin/perl

use strict;
use warnings;

use MyArray;

my $array = MyArray -> new();

print "Started:\n";
print join (",", @{ $array -> get_array()} ),"\n";

print "Reshuffling:\n";
$array -> sort_array();

$array -> push_array();
$array -> pop_array();

print "Finished:\n";
print join (",", @{ $array -> get_array()} ),"\n";

它可能会稍微整理一下,但希望这能说明 - 在您的对象中,您有一个内部 'array' 然后您可以通过调用 'do stuff with'。

结果大同小异(我想我已经复制了逻辑,但不要完全相信它!)但是你有一个独立的事情在进行。

如果该函数对 return 没有任何意义,则无需使用 return

不,您没有使用 OO 的任何方面(封装、多态性等)。您正在做的事情称为过程编程。没有错。我所有关于核电站的工作都是用这种风格写的。

问题在于使用 @main::array,我并不是说您可以将其缩写为 @::array。完全限定的名称可以逃避严格的检查,因此它们更容易出错。键入错误的 var 名称不会那么容易被发现,并且使用相同的变量名称很容易导致两段代码发生冲突。

如果您只使用一个文件,您可以使用 my @array,但我认为您使用的是 @main::array,因为您正在从多个 files/modules 访问它。我建议将 our @array 放在一个模块中,然后导出它。

package MyData;
use Exporter qw( import );
our @EXPORT = qw( @array );

our @array;

1;

在变量名中有某种提示(例如前缀或后缀)表明这是一个跨多个模块使用的变量会很好。


顺便说一句,如果你想创建一个对象,它看起来像

package MyArray;

sub new {
   my $class = shift;
   my $self = bless({}, $class);
   $self->{array} = [ @_ ];
   return $self;
}

sub get_elements {
   my ($self) = @_;
   return @{ $self->{array} };
}

sub sort {
   my ($self) = @_;
   @{ $self->{array} } = sort @{ $self->{array} };
}

sub push {
   my $self = shift;
   push @{ $self->{array} }, @_;
}

sub pop {
   my ($self, $n) = @_;
   return splice(@{ $self->{array} }, 0, $n//1);
}

my $array = MyArray->new(1,4,2,6,1,8,5,5,2);
$array->sort;
print("$_\n") for $array->get_elements();
$array->push_array(1..9);
$array->pop_array(3);

我改进了你的界面。 (排序不应该打印。推送不同的东西并弹出三个元素以外的东西会很好。)