【问题标题】:Should a subroutine always return explicitly?子程序是否应该总是显式返回?
【发布时间】:2015-04-29 23:21:50
【问题描述】:

如果perlcritic 说“在 sub 中没有返回是错误的”,如果真的不需要它们,还有什么替代方法?

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

  • 我明确地将变量分配给 '$main::' 命名空间。
  • 然后我在 subs 中使用这些变量。

例如,我可能会这样做..

#!/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 评论家说它错了 - 因为没有回报..

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

【问题讨论】:

  • 其实不需要返回值——见perldoc.perl.org/functions/return.html
  • 这不是 需要 但 perlcritic 标记它的原因是,使用隐式返回是不好的风格。例如。你用$result = $a + $b; 结束你的脚本——然后有人想调试它,弄清楚$result 是什么——他们在它后面添加一个print。这会更改脚本的返回码。最好明确一点。

标签: perl function oop subroutine perl-critic


【解决方案1】:

简而言之 - 是的,您基本上是在做 OO,但在某种程度上会让每个人都感到困惑。

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

这就是为什么要尽可能避免使用“全局变量”的原因。

对于一个简短的脚本,没关系太多

关于返回值——Perl 默认返回最后一个表达式的结果。 (见:return

(在没有显式返回的情况下,子例程、eval 或 do FILE 会自动返回最后计算的表达式的值。)

Perl critic标记它的原因是:

要求所有子例程以下列之一显式终止:return、carp、croak、die、exec、exit、goto 或 throw。

结尾处没有显式返回语句的子例程可能会令人困惑。推断返回值可能具有挑战性。

此外,如果程序员并不想有一个重要的返回值,并且省略了一个 return 语句,那么子例程的一些内部数据可能会泄漏到外部。

虽然 Perlcritic 并不总是正确的 - 如果你有充分的理由去做你正在做的事情,那么就把它关掉。只要您考虑过并意识到风险和后果。

我个人认为明确返回某些东西会更好,即使它只是return;

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

#!/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";

它可能会被整理一下,但希望这能说明 - 在你的对象中,你有一个内部“数组”,然后你可以通过调用来“做一些事情”。

结果大致相同(我想我已经复制了逻辑,但不要完全相信这一点!)但是你有一个独立的事情发生。

【讨论】:

  • 这很有意义!不幸的是,在这种情况下,我正在编写一个“大脚本”,所以看起来我可能需要重新编写 :) 感谢您提供详细的答案和示例。
  • retcon 很尴尬,而且一开始看起来很辛苦 - 确实如此 - 但随着复杂性的增加,它会带来好处,因为当出现问题时,你总是知道去哪里找。
  • 关于“关于返回值 - perl 隐式返回最后一个结果。这是一个等待发生的陷阱”,废话。
  • 嗯,这就是 perlcritic 给出的标记它的原因。 search.cpan.org/~thaljef/Perl-Critic-1.123/lib/Perl/Critic/…
【解决方案2】:

如果函数不返回任何内容,则无需使用return

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

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

如果您只使用一个文件,您可以使用my @array,但我认为您使用的是@main::array,因为您从多个文件/模块访问它。我建议将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);

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

【讨论】:

  • 我对做一些程序性的事情而不是面向对象的事情非常满意。我想我还是会尽量避免使用全局变量,只是为了尽量减少命名空间污染和远距离操作。
  • @Sobrique,没有全局变量就没有程序。也许你的意思是功能性的?老实说,人们忘记了全局对象是怎样的。对象可以被传递对象的任何模块修改,赋予它与全局变量几乎相同的范围。 OP 不应该将每个变量都设为全局变量,而应仅将主要数据结构设为全局变量。将其视为由内而外的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 2021-01-06
  • 2010-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多