arrays - 按值而不是索引对 Perl 数组进行切片的惯用方法

标签 arrays perl slice

我有一段代码,它从两个大的已排序整数数组之一中提取一个切片,表示程序工作流程中的停止点。我将在此处包含两者之一。

基本思想是,我试图从这个大数组中切出一个工作范围,作为该程序工作的起点。 $min从任务对象中拉取,表示任务的当前进度。 $limit是可选的用户覆盖,默认为 -1 (被忽略)。

目前,我正在使用 firstidx函数来自 List::MoreUtils CPAN 模块来检索开始和结束的索引,然后我使用它们来切片 @steps按照通常的方式排列。有没有更快和/或更惯用的方法来做到这一点?特别是,有没有直接使用 $min 来做到这一点的好方法?和$limit值(对于 $limit == -1 情况有另一个代码路径)?

代码如下:

my @steps = (
    0, 1, 5, 10,
    20, 30, 40, 50, 60, 70, 80, 90, 100,
    200, 300, 400, 500, 600, 700, 800, 900, 1000,
    1200, 1400, 1600, 1800, 2000,
    2500, 3000, 3500, 4000, 4500, 5000,
    6000, 7000, 8000, 9000, 10000,
    11000, 12000, 13000, 14000, 15000, 17500, 20000,
    25000, 30000, 35000, 40000, 45000, 50000,
    60000, 70000, 80000, 90000, 100000
);
my $min_index = firstidx { $_ > $min } @steps;
my $max_index;
if ($limit == -1) {
    $max_index = @steps - 1;
} else {
    $max_index = firstidx { $_ >= $limit } @steps;
}
my @steps_todo = @steps[ $min_index .. $max_index ];

最佳答案

您可以使用简单的 foreach 循环来完成此操作。流程控制关键字控制何时开始或停止使用 @steps 数组。

我将无限制情况更改为定义的检查,因为这样更有效。

这不是最有效的算法,但它是解决该问题的一种简单的惯用方法。

use diagnostics; # Very verbose warnings
($min, $max, @out) = (1000, 9999);

@steps = (
    0, 1, 5, 10,
    20, 30, 40, 50, 60, 70, 80, 90, 100,
    200, 300, 400, 500, 600, 700, 800, 900, 1000,
    1200, 1400, 1600, 1800, 2000,
    2500, 3000, 3500, 4000, 4500, 5000,
    6000, 7000, 8000, 9000, 10000,
    11000, 12000, 13000, 14000, 15000, 17500, 20000,
    25000, 30000, 35000, 40000, 45000, 50000,
    60000, 70000, 80000, 90000, 100000
);


foreach (@steps) {
    next if $_ < $min;
    last if defined $max and $_ > $max;
    push @out, $_;
}


print "@out";

## output
## 1000 1200 1400 1600 1800 2000 2500 3000 3500 4000 4500 5000 6000 7000 8000 9000


这也是触发器运算符 .. 的一个可能用例。它看起来像列表范围运算符,但在标量上下文中它是触发器。可以将其视为用于任意表达式的 cmp 三态运算符。它返回一个定义从“this”到“that”范围的值。称之为挡板。看看这个样本。请参阅perlop Range Operator部分了解更多信息。操作数可以是任何返回 bool 值的值。

use Data::Dump qw/pp/;
($min, $max) = (1000, 9999);
foreach (@steps) {
    my $x = ($_ >= $min .. $_ < $max);
    printf "%-9s %s\n", $_, pp $x;
}

## Output
0         ""
1         ""
5         ""
10        ""
20        ""
30        ""
40        ""
50        ""
60        ""
70        ""
80        ""
90        ""
100       ""
200       ""
300       ""
400       ""
500       ""
600       ""
700       ""
800       ""
900       ""
1000      "1E0"
1200      "1E0"
1400      "1E0"
1600      "1E0"
1800      "1E0"
2000      "1E0"
2500      "1E0"
3000      "1E0"
3500      "1E0"
4000      "1E0"
4500      "1E0"
5000      "1E0"
6000      "1E0"
7000      "1E0"
8000      "1E0"
9000      "1E0"
10000     1
11000     2
12000     3
13000     4
14000     5
15000     6
17500     7
20000     8
25000     9
30000     10
35000     11
40000     12
45000     13
50000     14
60000     15
70000     16
80000     17
90000     18
100000    19


请注意那些返回“1E0”的值,因为这些值会触发触发器中的复位。在真正的循环中,过渡点是它将终止的地方。您可以使用参数来选择哪组值将获得“”、“1E0”或序列号,并采取相应的操作。

此版本将序列号提供给想要的值,然后开始“E0”重置值。

($_ >= $min .. $_ > $max);

...
800       ""
900       ""
1000      1
1200      2
...
8000      15
9000      16
10000     "17E0"
11000     "1E0"
12000     "1E0"
...

以下是使用触发器解决该问题的方法。这种方式的优点是一旦达到下限条件就不再计算。主要缺点是它非常神秘,非 Perl 程序员可能无法理解。但这是尽可能紧凑和高效的。

foreach (@steps) {
    ($_ >= $min .. ($_ > $max and last)) or next;
    push @out, $_;
}

print "@out\n";
## 1000 1200 1400 1600 1800 2000 2500 3000 3500 4000 4500 5000 6000 7000 8000 9000


虽然左挡板为假,但整个表达式为假,我们进入下一项。当左挡板变为真时,整个表达式现在为真,我们继续并开始检查右挡板。虽然右挡板为 false(整个表达式仍然为 true),但我们继续将项目保存在 @out 中。当右挡板变为真时,我们终止循环。 (这也是它会重置回 false 的地方。)

再进行一次编辑。 :)

如果没有最大值,这里有一个好方法。


for (my $i = 0; $i <= $#steps; $i++) {
    next unless $steps[$i] >= $min;
    @out = @steps[$i .. $#steps];
    # Or, if you don't need the original array anymore you can consume it 
    # and be a bit more efficient and save memory
    # @out = splice @steps, $i;
    last;

}

好问题!

关于arrays - 按值而不是索引对 Perl 数组进行切片的惯用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69969430/

相关文章:

arrays - 循环将值复制到数组中

Android : how to create Multidimensional Arrays in string. xml

go - 我可以根据条件向创建语句中的 slice 添加项目吗?

python - 在 numpy 中移动图像

regex - perl 中正则表达式匹配的奇怪问题,替代尝试匹配

python - 为什么范围对象不能用于索引列表?

c++ - C++头文件中定义字符数组

javascript - ClojureScript 数组性能是否需要 mod - 或者只是关于边界检查?

perl - File::Tee 和打开到 "tee"的管道有什么区别吗?

javascript - 为什么这个正则表达式在有或没有连字符/破折号转义的情况下都可以工作