我正在寻找的是:
@list = qw(1 2 3 4 5 6);
foreach (@list) {
#perl magic goes here
print "i: $i, j:$j\n";
}
返回:
i:1, j:2
i:3, j:4
i:5, j:6
为了回应下面的一个非常好的建议,我需要指定此脚本将在其他人的构建服务器上运行,并且我不允许使用CPAN中的任何模块。仅限标准Perl。
答案 0 :(得分:38)
我认为正确的方法是使用来自List::MoreUtils的 来自文档: natatime BLOCK LIST 创建一个数组迭代器,用于在 示例: 打印
$n
项的块中循环数组
一次。 (n
一次,得到它?)。一个例子可能更好
解释比我能说的话。 my @x = ('a' .. 'g');
my $it = natatime 3, @x;
while (my @vals = $it->())
{
print "@vals\n";
}
a b c
d e f
g
sub natatime ($@)
{
my $n = shift;
my @list = @_;
return sub
{
return splice @list, 0, $n;
}
}
答案 1 :(得分:20)
我会使用拼接。
my @list = qw(1 2 3 4 5 6);
while(my ($i,$j) = splice(@list,0,2)) {
print "i: $i, j: $j\n";
}
答案 2 :(得分:15)
我认为你想要以不同的方式做到这一点。 试试这个:
while (scalar(@list) > 0) {
$i = shift(@list);
$j = shift(@list);
print "i: $i, j:$j\n";
}
请记住,这会破坏列表,但它适用于那个小循环。
答案 3 :(得分:15)
say
:use Modern::Perl;
use List::AllUtils qw'zip';
my @array = zip @{['a'..'z']}, @{[1..26]} ;
{
my $i = 0;
while(
(my($a,$b) = @array[$i++,$i++]),
$i <= @array # boolean test
){
say "$a => $b";
}
}
List::Pairwise
(pair)
。 use List::Pairwise qw'pair';
for my $pair (pair @array){
my($a,$b) = @$pair;
say "$a => $b";
}
List::MoreUtils
(natatime)
一次循环数组2。 use List::AllUtils qw'natatime';
my $iter = natatime 2, @array;
while( my($a,$b) = $iter->() ){
say "$a => $b";
}
{
my %map = @array;
for my $key (keys %map){
my $value = $map{$key};
say "$key => $value";
}
}
答案 4 :(得分:10)
不幸的是,最接近的等同物是老去的:
for(my $ix = 0; $ix <= $#list; $ix += 2) {
my $i = $list[$ix];
my $j = $list[$ix + 1];
print "i: $i, j:$j\n";
}
我更喜欢Jack M的答案,但是我会用更性感的Perl来写它:
while(@list) {
my $i = shift @list;
my $j = shift @list;
print "i: $i, j:$j\n";
}
答案 5 :(得分:7)
如果我只能使用没有模块的标准Perl,我可能会下降到一个C风格的循环,按2计算:
for( my $i = 0; $i < @array; $i += 2 ) { my( $i, $j ) = @array[ $i, $i+1 ]; ... }
但是,如果您想要使用其中一个无法使用的模块,您可以将该模块添加到您的代码中。如果您可以编写代码,则可以使用模块。您可能只需要在模块中包含您在适当设置@INC
时提供的所有代码。这是inc::Module::Install和PAR的基本概念。
我花了很多时间使用构建系统来创建自己的CPAN存储库,从其私有CPAN安装其依赖项,然后测试代码。拥有构建服务器场并不排除使用模块;这是当地的政策。但是,即使可能,这在所有情况下都没有意义。
答案 6 :(得分:4)
您可能想要创建一个简单的子程序,使其适合您。
我建议:
{
my $cl_ind = 0;
sub arrayeach(@) {
my @obj = @_;
if(($cl_ind+2) > @obj)
{
$cl_ind = 0;
return;
}
$cl_ind+=2;
return ($obj[$cl_ind-2],$obj[$cl_ind-1]);
}
}
封闭使其干净利落。要使用arrayeach(它的工作方式类似于散列,而不需要对数组进行危险的强制操作:
my @temp = (1,2,3,4,5,6,1,2,3,4,5,6);
while( ($a,$b) = arrayeach(@temp)) {
print "A $a AND $b\n";
}
这是非破坏性的。
答案 7 :(得分:4)
冒着死灵法术的标签,我决定再加上Tim Toady的背包:
for (0 .. $#list) {
next if $_ % 2;
my ($i, $j) = @list[$_, $_ + 1];
say "i:$i, j:$j";
}
非破坏性,无重复列表,无状态变量且合理简洁。
答案 8 :(得分:3)
通用功能解决方案如何。
use Carp; # so mapn can croak about errors
sub mapn (&$@) {
my ($sub, $n, @ret) = splice @_, 0, 2;
croak '$_[1] must be >= 1' unless $n >= 1;
while (@_) {
local *_ = \$_[0];
push @ret, $sub->(splice @_, 0, $n)
}
@ret
}
sub by ($@) {mapn {[@_]} shift, @_}
sub every ($@); *every = \&by;
mapn
函数的工作方式与map
类似,但块之后的第一个参数是要采用的元素数。它将第一个元素放在 $_
中,将所有元素放在 @_
中。
print mapn {"@_\n"} 2 => 1 .. 5;
# prints
1 2
3 4
5
接下来的两个相同的子by
和every
为各种循环结构创建有用的副词。他们使用mapn处理列表,并返回所需大小的数组引用列表
print "@$_\n" for every 2 => 1..10;
print map {"@$_\n"} grep {$_->[1] > 5} by 2 => 1..10;
我发现这是一个比natatime更清晰,更直观的解决方案,或者其他一种解决方案,比如c风格的循环。
答案 9 :(得分:3)
my $i;
for ( qw(a b c d) ) {
if (!defined($i)) { $i = $_; next; }
print STDOUT "i = $i, j = $_\n";
undef($i);
}
输出:
i = a, j = b
i = c, j = d
它也适用于列表,不仅适用于数组。
答案 10 :(得分:2)
正如Mirod所解释的那样,它没有太多代码。这几乎是你所需要的。 (请注意,我没有对奇数列表等进行任何检查。)
#!/usr/bin/env perl
use strict;
use warnings;
my @list = qw/1 2 3 4 5 6/;
my $get_em = get_by(2, @list);
while ( my ($i, $j) = $get_em->() ) {
print "i: $i, j: $j\n";
}
sub get_by {
my $n = shift;
my @list = @_;
return sub {
return splice @list, 0, $n;
}
}
答案 11 :(得分:1)
使用for循环可以满足您的需求。
use strict;
use warnings;
my @list = qw(1 2 3 4 5 );
my $i = 0;
for ($i = 0; $i < scalar(@list); $i++)
{
my $a = $list[$i];
my $b = $list[++$i];
if(defined($a)) {
print "a:$a";
}
if(defined($b)) {
print "b:$b";
}
print "\n";
}
编辑:我更正了帖子,使用标量函数检索数组的大小,并在数组不包含偶数元素的情况下添加一些检查。
答案 12 :(得分:1)
小型阵列的快速解决方案:
for ( map {$_*2} 0..@list/2-1 ){
my ($i, $j) = @list[$_,$_+1];
print "i: $i, j:$j\n";
}
某种oneliner
数据:
@v = (a=>1, b=>2, c=>3);
此
print join ', ', map{sprintf '%s:%s', $v[$_], $v[$_+1]} grep {!($_%2)} 0..$#v
或者像这样的事情
print join ', ', map {sprintf '%s:%s', @v[$_,$_+1]} map {$_*2} 0..@v/2-1;
结果相同
a:1, b:2, c:3
答案 13 :(得分:0)
我想出了这个代码来解决类似的要求:
sub map_pairs(&\@) {
my $op = shift;
use vars '@array';
local *array = shift; # make alias of calling array
return () unless @array;
# Get package global $a, $b for the calling scope
my ($caller_a, $caller_b) = do {
my $pkg = caller();
no strict 'refs';
\*{$pkg.'::a'}, \*{$pkg.'::b'};
};
# Get index counter size.
my $limit = $#array/2;
# Localize caller's $a and $b
local(*$caller_a, *$caller_b);
# This map is also the return value
map {
# assign to $a, $b as refs to caller's array elements
(*$caller_a, *$caller_b) = \($array[$_], $array[$_+1]);
$op->(); # perform the transformation
}
map { 2 * $_ } 0..$limit; # get indexes to operate upon.
}
你这样使用它:
@foo = qw( a 1 b 2 c 3 );
my @bar = map_pairs { "$a is $b" } @foo;
得到:
@bar = ( 'a is 1', 'b is 2', 'c is 3' );
我一直想提交给List :: MoreUtils的维护者,但我没有提供XS版本。
答案 14 :(得分:0)
这是natatime的一个实现,它没有复制列表:
sub natatime {
my $n = shift;
my $list = \@_;
sub {
return splice @$list, 0, $n;
}
}
my $it = natatime(3, qw(1 2 3 4 5 6));
while ( my @list = $it->() ) {
print "@list\n";
}
答案 15 :(得分:0)
我认为更简单的方法是使用旧的穷人&#39;每个人。
像这样:
while (my ($key,$value) = each @list) {
print "$key=$value\n";
}
更新:
是的,这是错的。首先应该将列表转换为哈希值,但它可能过于冒犯:
my %hash = (@list);
while (my ($key,$value) = each %hash) {
print "$key=$value\n";
}
答案 16 :(得分:0)
这可以非破坏性地完成,Eric Strom's简直太棒了List::Gen
:
perl -MList::Gen=":utility" -E '@nums = "1" .. "6" ;
say "i:$_->[0] j:$_->[1]" for every 2 => @nums'
<强>输出强>:
i:1 j:2
i:3 j:4
i:5 j:6
修改(添加无CPAN版本):
数组切片和C风格的循环àla brian d foy和Tom Christiansen!这可以理解为“使用索引($i
)一次循环@list
foreach
$n
个元素”:
use v5.16; # for strict, warnings, say
my @list = "1" .. "6";
my $n = 2 ; # the number to loop by
$n-- ; # subtract 1 because of zero index
foreach (my $i = 0 ; $i < @list ; $i += $n ) {
say "i:", [ @list[$i..$i+$n] ]->[0], " j:", [ @list[$i..$i+$n] ]->[1];
$i++ ;
}
我们将结果作为匿名数组(->[0]
)的元素([ ]
)来访问。对于更通用的输出,插值数组切片可以单独使用,例如:print "@list[$i..$i+$n]";
根据需要更改$n
的值。
答案 17 :(得分:0)
另一种方法,不完全清洁,但可用。每个都创建迭代器,你可以使用它两次。当参数是经典数组时,它返回索引和值,请阅读:https://perldoc.perl.org/functions/each.html
所以,你的代码可以是这样的:
my @array=qw(one two three four five); #five element as unpaired will be ignored
while (my ($i1,$one,$i2,$two)=(each(@array),each(@array)) {
#we will use $ix for detect end of array
next unless defined $i1 and defined $i2; #secure complete end of array
print "fetched array elements: $one => $two\n";
};
上面的例子不会破坏源数据,反对移位或类似。 我希望这对任何人都有帮助。当然,使用普通迭代器的情况要好得多。
答案 18 :(得分:-1)
while ($#rec>0) {
my($a,$b) = ( shift(@rec), shift(@rec) );
print "($a,$b)\n";
}