【问题标题】:Adaptative trim subroutine自适应修剪子程序
【发布时间】:2014-08-31 20:16:28
【问题描述】:

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

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

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

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/); (行为有待讨论)

这是我目前的丑陋解决方案:

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

【问题讨论】:

    标签: perl reference subroutine


    【解决方案1】:

    对我来说听起来太复杂了。用法必须比这更容易阅读和记住![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 中不是一个好主意。遇到重载字符串化的对象时,您的代码会出现意外行为。

    【讨论】:

      【解决方案2】:

      这个问题离题了,可能很快就会结束。

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

      我会说不。我还不清楚你希望这个子例程如何处理我面前的信息,而且,如果不参考你的文档,如果我没有使用它,我肯定会努力记住如何调用它一两天。你会发现大家都是通过写来使用的

      $string = trim($string)
      

      因为那是他们所记得的。

      您似乎也不清楚某些调用的作用,因此

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

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

      考虑到这一点,传递一个 reference 来指示它要被就地修改只不过是一个标志,它指示子例程的行为方式,以及引用必须先取消引用,然后才能使用。

      问问你自己,你经常看到一个常用的库函数有这样的行为。例如,有单独调用indexrindex来做非常相似的事情,而现有的核心运营商lctrim试图做的事情相当相似,但它只接受一个参数并返回转换后的结果。 sprintf 可以看作是printf 的变体,如果它知道,如果文件句柄参数是对标量的引用,它应该将字符串放入变量而不是写入它到一个文件句柄。但它不是那样设计的。

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

      还有一个问题要问自己,当一个引用被传递并且值被原地修改时,返回值应该是什么?很明显,它应该在更改后返回值,但是,由于您正在编写自适应代码,如何根据调用是在标量、列表还是 void 上下文中更改返回值?或者,更好的是,将trim 设为lvalue subroutine,这样您就可以编写类似

      my $n = q{               999                     };
      ++trim($n);
      

      【讨论】:

      • 我同意。支持一种或两种调用方式——我建议trim(\$inplace) 和/或$output = trim($input)。如果调用者需要修剪整个数组,Perl 可以使用非常好的 mapfor 关键字。
      • @tobyink:正如我试图暗示的那样,我不同意将引用仅作为标志来表示就地功能。 trim($string, inplace => 1)trim_inplace($string) 之类的都可以
      • @borodin,你为什么认为我的问题跑题了?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-24
      • 2015-05-05
      • 2012-10-27
      • 2011-02-19
      • 1970-01-01
      相关资源
      最近更新 更多