代码之家  ›  专栏  ›  技术社区  ›  Psytronic

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

  •  5
  • Psytronic  · 技术社区  · 16 年前

    所以我已经为Perl提供了一个简单的UCWords函数,我已经有一段时间了,想要扩展它,这就是我想到的,这是我构建函数来处理可选参数的方式吗?

    原件:

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

    扩展:

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

    PSY

    10 回复  |  直到 13 年前
        1
  •  25
  •   Community CDub    8 年前

    一句话: 不!

    让我们看看:

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

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

    第二,你没有使用 warnings . 用它。这是为了你自己。例如,第一个元素 @_ 应参考使用 $_[0] @_[0] .

    第三,在重新发明轮子之前,你应该养成偶尔阅读常见问题列表的习惯:参见 How do I capitalize all the words on one line?

    如果您认为这很严厉,请考虑以下事实:

    print ucwords("FRED AND BARNEY'S LODGE"), "\n";
    

    您的代码输出

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

    也见 Why use if-else if in C++?

        2
  •  11
  •   Sinan Ünür    16 年前

    不要使用 $foo ne undef 构造。Perl中的操作符被称为“上下文敏感”。通过使用某些运算符,可以引入某些上下文。 ne , eq , lt , gt , le , ge 都是“字符串”运算符,将两边的标量都视为字符串,而 == , != , < , > , <= , >= 是数字运算符,将两边的对象视为数字。

    但是,如果您正在测试UNdef,那么未定义的东西实际上是一个数字或字符串是没有意义的,因此它们有一个用于此类测试的运算符: defined

    你可以简单地通过做来测试某个东西是否被定义

    if (defined $foo) {
        # my cool logic on $foo here
    }
    
        3
  •  5
  •   Robert P    16 年前

    @_ 拆箱

    通常,在子例程中进行任何其他处理之前,您总是希望解压缩@。这使用户、其他维护人员和您自己在未来更加清楚地了解如何使用Sub。 @ 直接说来,很难从给出的参数中找出需要传递的内容。他们没有任何有意义的名字,这使得确定他们的目的更加困难,而且你到处都有魔法常数——通常情况下,这是一件坏事!

    最好的办法是在做其他事情之前,立即将变量放入有意义的scalar中。

    对于一个参数子例程,一个常见的解决方案是使用 shift . 这将提取数组的第一个元素,并返回它(类似于 pop 如果没有给定一个数组,而您在一个子例程中,它将从@数组中提取该数组。所以,你可以这样做

    sub mysub {
        my $foo = shift;
    }
    

    对于任何一个参数子例程。

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

    sub myothersub {
       my ($foo, $bar, $baz) = @_;
    }
    

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

        4
  •  5
  •   Sinan Ünür    16 年前

    这可能只是我的观点,您的编码风格完全取决于您,但就个人而言,我发现在将参数直接分配给变量,而不是将子例程的“业务”部分包装在if块中时有很多价值,在此之前,我会发出函数croak。例如:

    use Carp;
    
    sub ucwords {
        my $str = shift;
        defined($str) 
            or croak 'No overload method of ucwords() takes no arguments';
        #the rest of your logic
    }
    
        5
  •  5
  •   Robert P    13 年前

    Perl的switch语句:给定/时间

    从5.10及更高版本开始,Perl内置了一个很棒的switch语句,名为[ given ]这大致相当于 switch 用C语言表达,但更加通用。要启用此功能,需要在脚本顶部添加一行:

    use 5.010;
    

    这将启用所有Perl5.10特性,包括交换机(和 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; }
    }
    

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

    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 '$_'." }
    }
    
        6
  •  4
  •   Ivan Nevostruev    16 年前

    也许你会发现 Params::Validate 有用的它可以用于通过各种规则验证参数。以下是您的情况:

    ## somewhere is the start of the module
    use Params::Validate qw(:all);
    
    sub ucwords {
        ## this line helps to undestand which parameter should be passed to function
        my ($string, $algorithm_id) = @_; 
    
        ## make sure that 2 scalar parameters passed
        validate_pos(@_, {'type' => SCALAR}, {'type' => SCALAR});
    
        ## main code here
    }
    
        7
  •  4
  •   Robert P    16 年前

    die

    死亡 与其他Perl内置函数一样,不需要,通常不应该有括号。然而, 死亡 有一个现在大多数人都用的大哥,叫

    croak

    做:

    use Carp;
    

    然后

    croak "My error here!";
    

    croak的工作原理与die类似,但通常会向错误消息添加比 死亡 是,例如相对于调用方发生错误的行。

        8
  •  3
  •   Sinan Ünür    16 年前

    数组索引

    数组访问和Perl中的其他东西一样,是上下文敏感的。把这个附加在名字上的符号想象成一个“提醒”,提醒你现在想要访问或使用什么。每当你看到 $ ,这意味着您正在尝试获取单个标量值。每当你看到 @ ,这意味着您正在访问列表,并且 % 当然,这意味着一个键/值哈希对。因此,当您像这样访问您的数组时:

    @_[1]
    

    您需要一个包含单个元素的列表。这个特性允许您一次从一个数组中获取多个值,但是当只访问一个值时,它会在某些上下文中引起问题,例如赋值。因此,在访问单个数组元素时,您希望始终使用标量上下文:

    $_[1]
    
        9
  •  2
  •   Schwern    16 年前

    其他答案中已经暗示但没有直接提到的是数字模式的使用,这是Perl从C.Quick继承过来的一种惯例,不看代码,模式3做什么?见鬼,看看代码模式3有什么作用?

    Perl具有高效且易于使用的字符串。使用它们。给你的模式命名与它的作用有关。有点像…先,后,先,后。它们不必完全是描述性的,小写字母,那么最后一个字母就太长了,无法打字,但给了人类足够的东西,让大脑可以依附和联想。

    但实际上这是四个子例程。模式标志是红色标志,特别是当大多数代码都出现在if/else语句中时。

        10
  •  2
  •   Sinan Ünür    15 年前

    我非常不喜欢过于智能的功能。过于智能的函数是其行为完全由其参数改变的函数。看看你的,除了参数处理,它们几乎不共享任何代码。不管怎样,如果我做一些类似的事情,我会写如下:

    use Carp;
    
    {
        my %ucwords = (
            0 => sub {
                my $str = lc( shift() );
                $str =~ s/\b(\w)/\u$1/g;
                return $str;
            },
            1 => sub {
                my $str = shift;
                $str =~ s/\b(\w)/\u$1/g;
                return $str;
            },
            2 => sub {
                $str = lc( shift() );
                $str =~ s/(\w)\b/\u$1/g;
                return $str;
            },
            3 => sub {
                my $str = shift;
                $str =~ s/(\w)\b/\u$1/g;
                return $str;
            }
        );
    
        sub ucwords {
            my ( $str, $mode ) = @_;
            croak "No overload method of ucwords() takes no arguments"
                unless defined $str;
            $mode = 0 unless defined $mode;
            my $code = $ucwords{$mode};
            croak "Invalid mode: '$mode'" unless defined $code;
            goto \&$code;
        }
    }
    
    推荐文章