自适应修剪子程序

时间:2014-08-31 20:16:28

标签: perl reference subroutine

通过这个例子,我想学习编写适应性子程序的最佳方法。

我需要一个修剪文本的子程序(实际上,它是一个借口,我的子程序可以做任何其他事情)。

为了更通用,我希望我的子程序接受不同类型的参数:

my @input = (' A ', ' B', 'C ');

  1. my @trimmed = trim(@input);

  2. trim(\@input);

  3. my $out = trim($input[0]);返回' A'

  4. trim(\$input[0]);修剪@input的第一个元素;

  5. trim(\@a, \@b, \@c);每个字符串数组都被修剪

  6. my $out = trim(\@a, ' A ', ' B'); $ out =(qw / A B /); (要讨论的行为)

  7. 这是我目前难看的解决方案:

    sub trim {
        state $re = qr/^\s+|\s+$/m;
        my @a;
        for(@_) {
            if(ref eq 'SCALAR') { $$_ =~ s/$re//g;    }
            elsif(ref)          { trim(\$_) for(@$_); }
            else                { push @a, s/$re//gr; }
        }
        return \@a if @a > 1;
        return $a[0] if $a[0];
    }     
    

    如上所述,是否有更好的解决方案支持不同类型的输入?

    这个问题的主要原因涉及我的最终应用程序,其中我携带的文本可以存储在字符串,字符串数组甚至哈希中。

    我想写一下会更好:

    trim(\@allmytexts);
    align(\@allmytexts, align=>'right');
    

    比:

    for(@allmytexts) {
       trim($_);
       align($_, align=>'right');
    }
    

2 个答案:

答案 0 :(得分:2)

听起来对我来说太复杂了。使用必须比那更容易阅读和记忆! [1] 。只需trim取标量(默认为$_)。

sub trim(_) { $_[0] =~ s/^\s+|\s+\z//rg }

开始,所有案例都可以轻松处理
$x = trim($x);
@a = map trim, @a;

您也可以制作就地版本。

sub trim_i(_) { s/^\s+|\s+\z//g for $_[0] }

这将使用如下:

trim_i($x);
trim_i for @a;

另一种可能性是获取标量列表(无默认值)。

sub trim {
   wantarray
      ? map s/^\s+|\s+\z//rg, @_
      : $_[0] =~ s/^\s+|\s+\z//rg
}

$x = trim($x);
@a = trim(@a);

sub trim_i {
   s/^\s+|\s+\z//rg for @_;
}

trim_i($x);
trim_i(@a);

但它有点奇怪。特别是因为my $x = trim($y, $z);是可能的。


  1. 此外,它使用基于类型的多态,这在Perl中不是一个好主意。当遇到重载字符串化的对象时,您的代码会出现意外行为。

答案 1 :(得分:1)

这个问题不合时宜,很快就会结束。

然而,“在这样的子程序中要求这种行为是否正确?”

我会说不。我很清楚你想要这个子程序在我面前的信息是什么,并且,如果我没有使用它,我肯定会努力记住如何调用它。一两天。你会发现每个人都通过写

来使用它
$string = trim($string)

因为这是他们记得的。

看起来你自己也不清楚某些电话是做什么的,因此

my $out = trim(\@a, ' A ', ' B'); $out = (qw/A B/); (behavior to be discussed)

我怀疑你习惯了一种具有不同传递机制的语言,这样的东西会很有用。在Perl中,所有都由别名传递;因此对@_数组元素的操作等同于对实际参数的相同操作。这意味着如果将该值作为参数传递,子例程可以修改任何可写值。

考虑到这一点,传递引用以指示它将被就地修改,只不过是一个标志,指示子例程的行为方式,以及该例程的阻碍。在使用之前必须取消引用引用。

问问自己,您经常看到常用库函数的行为是这样的。例如,对indexrindex进行单独的调用以执行非常类似的操作,并且现有核心运算符lc与{{1}相似尝试做但它只需要一个参数并返回转换后的结果。 trim可以被视为sprintf的变体,只要它知道,如果文件句柄参数是对标量的引用,它应该将字符串放入变量而不是将其写入文件句柄。但它不是那样设计的。

我只能为已定义的问题提供替代解决方案。你没有解释现实生活中的困难,因为你不确定自己printf应该做什么,我怀疑是否有人可以帮你改进它。

要问自己的其他事情是,当传递引用并且值被修改到位时,返回值应该是多少?很明显它应该在更改后返回值,但是,由于您正在编写自适应代码,如何根据调用是在标量,列表还是无效上下文中更改返回值?或者,更好的是,将trim设为lvalue subroutine,以便您可以编写类似

的内容
trim