这是构建Perl子程序的方法吗?

Psy*_*nic 5 parameters perl function user-defined-functions

所以我有一个简单的ucwords函数用于Perl我已经有一段时间了,想要扩展它,这就是我想出来的,这是我应该构建我的函数来处理可选参数的方式吗?

原版的:

sub ucwords{
    $str = @_[0];
    $str = lc($str);
    $str =~ s/\b(\w)/\u$1/g;
    return $str;
}
Run Code Online (Sandbox Code Playgroud)

扩展:

sub ucwords{
    if(@_[0] ne undef){#make sure some argument was passed
        @overloads = (0,1,2,3);
        $str = @_[0];
        if(@_[1] eq undef || @_[1] eq 0){ #default is to lowercase all but first
            $str = lc($str);
            $str =~ s/\b(\w)/\u$1/g;
            return $str;
        }else{ #second parameters
            if(!grep $_ eq @_[1], @overloads){ die("No overload method of ucwords() takes ".@_[1]." as second parameter."); }
            if(@_[1] eq 1){ $str =~ s/\b(\w)/\u$1/g;} #first letter to upper, remaining case maintained
            if(@_[1] eq 2){ $str = lc($str); $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining to lower
            if(@_[1] eq 3){ $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining case maintained
            return $str;
        }
    }else{
        die("No overload method of ucwords() takes no arguments");
    }
}
Run Code Online (Sandbox Code Playgroud)

精神科

Sin*_*nür 25

一句话:不!

我们来看看:

sub ucwords{
    $str = @_[0];
    $str = lc($str);
    $str =~ s/\b(\w)/\u$1/g;
    return $str;
}
Run Code Online (Sandbox Code Playgroud)

首先,你没有使用strict.用它.这是为了你自己的利益.

其次,你没有使用warnings.用它.这是为了你自己的利益.例如,@_应该使用$_[0] 使用第一个元素@_[0].

第三,你应该养成在重新发明轮子之前偶尔阅读常见问题清单的习惯:请参阅如何将一行中的所有单词大写?

如果您认为这是严酷的,请考虑以下事实:

print ucwords("FRED AND BARNEY'S LODGE"), "\n";
Run Code Online (Sandbox Code Playgroud)

你的代码输出

Fred And Barney'S Lodge

这是该问题中给出的例子.

此外,具有不止一件事的功能,根据神秘数字选择它所做的事情,并且这些事情都不是一个好的设计策略.

相反,您应该拥有多个函数,这些函数的命名方式可以由代码的随意读者理解,每个函数只执行一项操作并且正确执行.

最后,你的函数的扩展版本(没有说任何关于编写这样一个函数的智慧)可以更好地写成:

# untested code follows

use Carp;

{
    my %modes = map {$_ => undef} 0 .. 3;
    sub ucwords{
        croak 'No arguments passed' unless @_;

        my ($str, $mode) = @_;
        $mode = 0 unless defined $mode;

        croak "Invalid mode: '$mode'" unless exists $modes{$mode};

        if ( $mode == 0 ) {
            $str = lc($str);
            $str =~ s/\b(\w)/\u$1/g;
        }
        elsif ( $mode == 1 ) {
            $str =~ s/\b(\w)/\u$1/g;        
        }
        elsif ( $mode == 2 ) {
            $str = lc($str); 
            $str =~ s/(\w)\b/\u$1/g;        
        }
        else {
            $str =~ s/(\w)\b/\u$1/g;
        }

        return $str;
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅为什么在C++中使用if-else?

  • 我想你应该在这种情况下回答两次.一次使用原始答案,一次使用当前版本.在这种情况下,你本可以赢得两个赞成票. (4认同)

Rob*_*t P 11

不要使用该$foo ne undef构造.Perl中的运算符就是所谓的"上下文敏感".通过使用某些运算符,您可以引入某些上下文. ne,eq,lt,gt,le,ge全部为"串"操作符,处理在任一侧上为字符串的标量,而==,!=,<,>,<=,>=是数值运算,处理所述对象上的任一侧上为一个数字.

但是,如果你正在测试undef,那么undefined是一个数字或一个字符串是没有意义的,所以他们只有一个运算符用于那种测试: defined

您可以通过执行来测试是否定义了某些内容

if (defined $foo) {
    # my cool logic on $foo here
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*int 5

这可能只是我的意见,你的编码风格完全取决于你,但我个人觉得很有价值,可以立即将参数分配给变量,而不是将子程序的"业务"部分包装在一个如果阻止,我之前会有功能.例如:

use Carp;

sub ucwords {
    my $str = shift;
    defined($str) 
        or croak 'No overload method of ucwords() takes no arguments';
    #the rest of your logic
}
Run Code Online (Sandbox Code Playgroud)

  • 同意; 我总是怀疑远距离动作,所以我总是抓住特殊变量的本地副本,例如$ 1,$ _.我直接使用$ _的唯一地方就是单行地图或grep. (3认同)

Rob*_*t P 5

Perl的switch语句:给定/何时

从5.10及更高版本开始,Perl内置了一个非常棒的switch语句,称为[ given].这大致相当于switchC语句,但功能更多.要启用此功能,您需要在脚本顶部添加一行:

use 5.010;
Run Code Online (Sandbox Code Playgroud)

这启用了所有perl 5.10功能,包括switch(和say,它的工作方式类似,print但最后会自动添加"\n".)您可以像这样使用它:

my $foo = get_foo();
my $nothing = 0;
given($foo) {
    when (undef)  { say "got an undefined value!"; }
    when ([1,3,5,6,8]) { say "was 1, 3, 5, 6, or 8"; }
    when (/^abc/) { say "was a string starting with abc"; }
    when ($_ == 4) { say "It was 4!!!"; }
    when ($_ > 100) { say "Greater than 100"; }
    default { $nothing = 1; }
}
Run Code Online (Sandbox Code Playgroud)

传递给给定的变量会自动放入$_给定代码中,允许您与之进行比较.然后,when构造与$_.So 进行智能匹配,在您的情况下,它看起来像这样(将@ []修复为$ []问题):

given ($_[1]) {
    when (1) { $str =~ s/\b(\w)/\u$1/g }
    when (2) { $str = lc($str); $str =~ s/(\w)\b/\u$1/g }
    when (3) { $str =~ s/(\w)\b/\u$1/g; }
    default { croak "No overloaded method of ucwords() takes '$_'." }
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*t P 5

@_ 开箱

通常,在子例程中进行任何其他处理之前,总是要解压缩@_.这使得用户,其他维护者以及将来如何使用您的sub更加清晰.通过@_直接使用,很难根据给出的参数找出需要传递的内容.他们没有任何有意义的名字,因此更难确定他们的目的,你到处都有魔法常数 - 通常总体上是坏事!

最好的办法是在你做任何其他事情之前立即将变量变成有意义的命名标量.

对于一个参数子例程,一个常见的解决方案是使用shift.这会拉出数组的第一个元素,并返回它(类似于相反的情况pop.)如果没有给出一个数组,并且你在一个子程序中,它会从@_数组中取出它.所以,你可以做到

sub mysub {
    my $foo = shift;
}
Run Code Online (Sandbox Code Playgroud)

对于任何一个参数子程序.

但是,如果你有更多呢?列出上下文分配,来救援!可以使用列表赋值一次分配多个变量.你可以做

sub myothersub {
   my ($foo, $bar, $baz) = @_;
}
Run Code Online (Sandbox Code Playgroud)

$foo,$bar以及$baz将被分别分配在所述@_ 0,1和2的索引的值.那么,如果0,1或2索引中没有任何内容会发生什么?他们仍然被分配 - 他们成了undef!然后你可以检查这个问题中其他地方提到的undef.