【问题标题】:using references to point to sliding window array in Perl在 Perl 中使用引用指向滑动窗口数组
【发布时间】:2012-12-30 11:20:47
【问题描述】:

这是我的问题:我有 2 个数组。一个是字符数组,代表一个滑动窗口。字符从一开始就被移动并在最后被推动。我想使用第二个数组来存储对数组切片的引用,这些切片在字符移动时“跟随”字符。示例:

my @char_array = ('h','e','l','l','o','w','o','r','l','d');
my $char_arr_ref=[@char_array[1..$#char_array]]; 
print @$char_arr_ref, "\n"; # slice contains 'elloworld';
shift(@char_array);
push(@char_array), 'x';
print @$char_arr_ref, "\n"; # slice still contains 'elloworld', not 'lloworldx' as I need;

换句话说,我希望能够使用第二个数组来引用数组切片(例如,我会使用 C 中的指针数组)。

在 Perl 中有没有一种惯用的方法来做到这一点?

更新:这是进行快速文本搜索的大型程序的一部分。我打算使用引用的哈希(例如,而不是非常缓慢的“索引”函数。我需要在 Perl 中执行此操作。

【问题讨论】:

  • 也许你应该解释你想要达到的目标。我非常怀疑这最好通过引用来解决。

标签: perl reference character-arrays slice


【解决方案1】:

在 C 中,您的窗口可能使用指针算法来实现。

const char* s = str+1;
const char* e = str+len;
for (const char* p=s; p!=e; ++p) putc(*p);

除了指针算法不允许您调整缓冲区大小 (push @char_array, 'x';)。即使在 C 中,您也必须使用偏移量。

size_t si = 1;
size_t ei = len;
for (size_t i=si; i!=e1; ++i) putc(str[i]);

这是幸运的,因为 Perl 没有指针,更不用说指针运算了。但是抵消?没问题!

my @char_array = split //, 'helloworld';
my ($s, $e) = (1, $#char_array);
say @char_array[$s..$e];    # elloworld
shift @char_array;
push @char_array, 'x';
say @char_array[$s..$e];    # lloworldx

如果我们真的在谈论字符,字符串会更有效。

my $char_array = 'helloworld';
my ($s, $e) = (1, length($char_array));
say substr($char_array, $s, $e-$s+1);    # elloworld
$char_array =~ s/^.//s;
$char_array .= 'x';
say substr($char_array, $s, $e-$s+1);    # lloworldx

事实上,如果我们真的在谈论字符,我们很幸运,因为我们可以使用左值 substr 并让 Perl 为我们处理偏移量!

my $char_array = 'helloworld';
my $substr_ref = \substr($char_array, 1, length($char_array)-1);
say $$substr_ref;        # elloworld
$char_array =~ s/^.//s;
$char_array .= 'x';
say $$substr_ref;        # lloworldx

比 C 简单得多,但好处几乎相同!

【讨论】:

  • 这个解决方案确实完全太棒了! (虽然它不是很明显,并且只有在重新考虑大约三遍后才有意义。它真的可以在大多数/所有 perls 上移植吗?)
  • @amon,是的,便携。这就是 substr() = ...; 工作的原因。
  • 谢谢 ikegami。这就是我要找的。​​span>
【解决方案2】:

我不确定你在做什么,我怀疑它是否适合“快速文本搜索”程序。但是,您可以通过使用简单的子例程而不是引用来完成您想要的:

#!usr/bin/perl
use strict;
use warnings;

my @char_array = ('h','e','l','l','o','w','o','r','l','d');
sub char_arr_slice { return @char_array[1..$#char_array] };

print char_arr_slice, "\n"; 

shift(@char_array);
push(@char_array, 'x');

print char_arr_slice, "\n";

注意:为什么我会有疑问?因为字符数组很少是在 Perl 中处理字符串的正确方法。与使用 Perl 的内置字符串处理工具(尤其是正则表达式)相比,这种方法可能效率较低且笨拙得多。

【讨论】:

    【解决方案3】:

    这是一个使用重载对象的实现:

    #!/usr/bin/perl
    use strict; use warnings; use feature 'say';
    
    my @array = qw( H e l l o W o r l d );
    my $window = SlidingWindow->new(\@array, 1, -1);
    say "@$window";
    shift @array;
    push @array, "x";
    say "@$window";
    
    {
        package SlidingWindow;
        use overload '@{}' => sub {
            my ($self) = @_;
            # manage negative indices
            my $min = $self->{min} >= 0 ? $self->{min}
                                        : $#{ $self->{array} } + 1 + $self->{min};
            my $max = $self->{max} >= 0 ? $self->{max}
                                        : $#{ $self->{array} } + 1 + $self->{max};
            return +[ @{ $self->{array} }[$min .. $max] ];
        };
        sub new {
            my ($class, $arrayref, $min, $max) = @_;
            return bless +{
                array => $arrayref,
                min => $min,
                max => $max,
            } => $class;
        }
    }
    

    输出:

    e l l o W o r l d
    l l o W o r l d x
    

    当然,您有方法调用的开销,但 Perl 根本没有指针。如果你抱怨index 太慢(不能再快了),你只能改进你的算法,而不是你的实现。

    更新

    Ikegami 指出substr 可能是一个可行的选择。下面的解决方案没有实际使用数组的优点,但是我们使用 string 来代替字符数组。这在 Perl 中是不一样的:字符串更有效率。

    my $charray = "HelloWorld";
    say substr($charray, 1);
    substr($charray, 0, 1) = "";
    $charray .= "x";
    say substr($charray, 1);
    

    输出:

    elloWorld
    lloWorldx
    

    【讨论】:

    • 我实际上提到了 lvalue substr。有关详细信息,请参阅我的答案。我想你会印象深刻的。基本上,Perl 有一个用于字符串的内置 SlidingWindow!
    猜你喜欢
    • 2017-08-09
    • 2012-11-09
    • 2020-12-25
    • 2021-01-15
    • 2014-09-09
    • 1970-01-01
    • 2011-11-24
    • 2021-10-29
    相关资源
    最近更新 更多