私が探しているのは次のようなものです。
@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のみ。
これを行う適切な方法は、 List :: MoreUtils :からnatatimeを使用することだと思います。
ドキュメントから:
natatimeブロックリスト
一度に
$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
List::MoreUtils::natatime
の実装:
sub natatime ($@)
{
my $n = shift;
my @list = @_;
return sub
{
return splice @list, 0, $n;
}
}
スプライスを使います。
my @list = qw(1 2 3 4 5 6);
while(my ($i,$j) = splice(@list,0,2)) {
print "i: $i, j: $j\n";
}
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";
}
}
これを別の方法で行いたいと思います。これを試して:
while (scalar(@list) > 0) {
$i = shift(@list);
$j = shift(@list);
print "i: $i, j:$j\n";
}
これによりリストが破棄されますが、その小さなループでは機能することに注意してください。
最も近い同等物は、残念ながら、古い学校に行くことです:
for(my $ix = 0; $ix <= $#list; $ix += 2) {
my $i = $list[$ix];
my $j = $list[$ix + 1];
print "i: $i, j:$j\n";
}
私はジャックMの答えの方が本当に好きですが、もっとセクシーなPerlで書きます。
while(@list) {
my $i = shift @list;
my $j = shift @list;
print "i: $i, j:$j\n";
}
モジュールなしで標準のPerlしか使用できない場合は、おそらく2でカウントするCスタイルのforループにドロップダウンします。
for(my $ i = 0; $ i <@array; $ i + = 2){ my($ i、$ j)= @array [$ i、$ i + 1 ]; ... }
ただし、使用できないモジュールの1つから何か凝ったものが必要な場合は、そのモジュールをコードに追加するだけです。コードを書くことができれば、モジュールを使うことができます。 @INC
を適切に設定するときに、提供するすべてのコードにモジュールを含める必要がある場合があります。これは inc :: Module :: Install および [〜#〜] par [〜#〜] の基本的な考え方です。
私は、独自のCPANリポジトリを作成し、その依存関係をプライベートCPANからインストールしてから、コードをテストするビルドシステムで多くの時間を費やしています。ビルドファームがあるからといって、モジュールの使用が妨げられるわけではありません。そうするのはローカルポリシーです。ただし、それが可能であっても、すべての場合に意味があるとは限りません。
ネクロマンシータグを危険にさらして、TimToadyのバックパックからもう1つ追加することにしました。
for (0 .. $#list) {
next if $_ % 2;
my ($i, $j) = @list[$_, $_ + 1];
say "i:$i, j:$j";
}
非破壊、重複リスト、状態変数がなく、適度に簡潔です。
簡単なサブルーチンを作成して、それを機能させることをお勧めします。
私はこれを提案します:
{
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";
}
これは非破壊的です。
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
配列だけでなく、リストでも機能します。
汎用の機能ソリューションはどうですか。
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
次の2つの同一のサブ、by
とevery
は、さまざまなループ構造に役立つ副詞を作成します。それらはmapnでリストを処理し、目的のサイズの配列参照のリストを返します
print "@$_\n" for every 2 => 1..10;
print map {"@$_\n"} grep {$_->[1] > 5} by 2 => 1..10;
これは、natatimeや、cスタイルのforループのような他の1回限りのソリューションよりもクリーンで直感的なソリューションであることがわかりました。
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;
}
}
小さなアレイの迅速な解決策:
for ( map {$_*2} 0..@list/2-1 ){
my ($i, $j) = @list[$_,$_+1];
print "i: $i, j:$j\n";
}
ある種のワンライナー
データ:
@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
別のアプローチ、完全にクリーンではありませんが、使用可能です。それぞれがイテレータを作成し、2回使用できます。パラメータがクラシック配列の場合、インデックスと値を返します。以下をお読みください: 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";
};
上記の例では、シフトなどに対してソースデータを破棄しません。これが誰にとっても役立つことを願っています。もちろん、プレーンイテレータを使用した場合の方がはるかに優れています。
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";
}
編集:スカラー関数を使用して配列のサイズを取得し、配列に偶数の要素が含まれていない場合のチェックを追加するように投稿を修正しました。
もっと簡単な方法は、古い貧しい「それぞれ」を使うことだと思います。このようにまっすぐ:
while (my ($key,$value) = each @list) { print "$key=$value\n"; }
更新しました:
はい、それは間違っています。最初にリストをハッシュに変換する必要がありますが、コストがかかりすぎる可能性があります。
my %hash = (@list);
while (my ($key,$value) = each %hash) {
print "$key=$value\n";
}
リストのコピーを作成しない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";
}
私は同様の要件を解決するためにこのコードを思いついた:
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バージョンがありません。
これは非破壊的に行うことができ、 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スタイルのforループàlabrian 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
の値を変更します。