Should a subroutine always return explicitly?

只谈情不闲聊 提交于 2019-12-06 14:28:48

In short - yes, you're basically doing OO, but in a way that's going to confuse everyone.

The danger of doing subs like that is that you're acting at a distance. It's a bad coding style to have to look somewhere else entirely for what might be breaking your code.

This is generally why 'globals' are to be avoided wherever possible.

For a short script, it doesn't matter too much.

Regarding return values - Perl returns the result of the last expression by default. (See: return)

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

The reason Perl critic flags it is:

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 isn't always right though - if there's good reason for doing what you're doing, then turn it off. Just as long as you've thought about it and are aware of the risks an consequences.

Personally I think it's better style to explicitly return something, even if it is just return;.

Anyway, redrafting your code in a (crude) OO fashion:

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

And then call it with:

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

It can probably be tidied up a bit, but hopefully this illustrates - within your object, you've got an internal 'array' which you then 'do stuff with' by making your calls.

Result is much the same (I think I've replicated the logic, but don't trust that entirely!) but you have a self contained thing going on.

If the function doesn't mean to return anything, there's no need to use return!

No, you don't use any aspects of OO (encapsulation, polymorphism, etc). What you are doing is called procedural programming. Nothing wrong with that. All my work for nuclear power plants was written in that style.

The problem is using @main::array, and I'm not talking about the fact that you could abbreviate that to @::array. Fully-qualified names escape strict checks, so they are far, far more error-prone. Mistyped var name won't get caught as easily, and it's easy to have two pieces of code collide by using the same variable name.

If you're just using one file, you can use my @array, but I presume you are using @main::array because you are accessing it from multiple files/modules. I suggest placing our @array in a module, and exporting it.

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

our @array;

1;

Having some kind of hint in the variable name (such as a prefix or suffix) indicating this is a variable used across many modules would be nice.


By the way, if you wanted do create an object, it would look like

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);

I improved your interface a bit. (Sorting shouldn't print. Would be nice to push different things and to pop other than three elements.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!