質問
実際にはマップ関数のポイントを取得していません。誰でもその使用例を説明できますか?
ループの代わりにこれを使用することでパフォーマンス上の利点はありますか、それとも単なる砂糖ですか?
解決
別のリストに基づいてリストを生成したいときはいつでも:
# Double all elements of a list
my @double = map { $_ * 2 } (1,2,3,4,5);
# @double = (2,4,6,8,10);
特定の属性に基づいたオブジェクトのハッシュテーブルが必要な場合、リストはペアごとにハッシュに簡単に変換されるため、
# @user_objects is a list of objects having a unique_id() method
my %users = map { $_->unique_id() => $_ } @user_objects;
# %users = ( $id => $obj, $id => $obj, ...);
これは非常に汎用的なツールです。アプリケーションで適切な用途を見つけるには、使用を開始する必要があります。
読みやすくするために冗長ループコードを好む人もいますが、個人的にはmap
の方が読みやすいと思います。
他のヒント
まず第一に、それは配列を変換する簡単な方法です:例えば言うのではなく
my @raw_values = (...);
my @derived_values;
for my $value (@raw_values) {
push (@derived_values, _derived_value($value));
}
言うことができます
my @raw_values = (...);
my @derived_values = map { _derived_value($_) } @raw_values;
これは、クイックルックアップテーブルを構築するのにも役立ちます:例:
my $sentence = "...";
my @stopwords = (...);
my @foundstopwords;
for my $word (split(/\s+/, $sentence)) {
for my $stopword (@stopwords) {
if ($word eq $stopword) {
push (@foundstopwords, $word);
}
}
}
言うことができます
my $sentence = "...";
my @stopwords = (...);
my %is_stopword = map { $_ => 1 } @stopwords;
my @foundstopwords = grep { $is_stopword{$_} } split(/\s+/, $sentence);
また、あるリストを別のリストから派生させたいが、場所を乱雑にする一時的な変数を特に必要としない場合にも役立ちます。
ではなくmy %params = ( username => '...', password => '...', action => $action );
my @parampairs;
for my $param (keys %params) {
push (@parampairs, $param . '=' . CGI::escape($params{$param}));
}
my $url = $ENV{SCRIPT_NAME} . '?' . join('&', @parampairs);
はるかに単純なことを言う
my %params = ( username => '...', password => '...', action => $action );
my $url = $ENV{SCRIPT_NAME} . '?'
. join('&', map { $_ . '=' . CGI::escape($params{$_}) } keys %params);
(編集:最後の行の欠落している<!> quot; keys%params <!> quot;を修正しました)
map
関数は、リストの変換に使用されます。基本的に、特定のタイプのfor[each]
ループを置き換えるための構文糖衣です。頭を包むと、どこにでも使用できることがわかります。
my @uppercase = map { uc } @lowercase;
my @hex = map { sprintf "0x%x", $_ } @decimal;
my %hash = map { $_ => 1 } @array;
sub join_csv { join(',', map {'"' . $_ . '"' } @_ }
マップの高度な使用法については、シュワルツ変換もご覧ください。
ルックアップハッシュの作成にも便利です:
my %is_boolean = map { $_ => 1 } qw(true false);
は同等です
my %is_boolean = ( true => 1, false => 1 );
そこにはあまり節約はありませんが、%is_US_state
を定義したいとしますか?
map は、別のリストの要素を変換してリストを作成するために使用されます。
grep は、別のリストの要素をフィルタリングしてリストを作成するために使用されます。
sort は、別のリストの要素をソートしてリストを作成するために使用されます。
これらの演算子はそれぞれ、リストの要素の変換、フィルタリング、比較に使用されるコードブロック(または式)を受け取ります。
map の場合、ブロックの結果は新しいリスト内の1つ(または複数)の要素になります。現在の要素は$ _にエイリアスされています。
grep の場合、ブロックのブール結果は、元のリストの要素を新しいリストにコピーするかどうかを決定します。現在の要素は$ _にエイリアスされています。
sort の場合、ブロックは2つの要素(エイリアス$ aおよび$ b)を受け取り、$ aがより大きいか、等しいか、または$ b未満。
Schwartzian Transform は、これらの演算子を使用して、並べ替えに使用する値(プロパティ)を効率的にキャッシュします。リスト、特にこれらのプロパティを計算する場合は、簡単なコストがかかります。
これは、元の要素と計算した値を並べ替える配列参照を要素として持つ中間配列を作成することで機能します。この配列はsortに渡され、既に計算された値を比較し、別の中間配列(これはソートされます)を作成し、キャッシュされた値を破棄する別のマップに渡され、初期リスト要素に配列を復元します(ただし、今すぐ希望の順序で)。
例(現在のディレクトリにファイルのリストを作成し、最後に変更した時刻でソートします):
@file_list = glob('*');
@file_modify_times = map { [ $_, (stat($_))[8] ] } @file_list;
@files_sorted_by_mtime = sort { $a->[1] <=> $b->[1] } @file_modify_times;
@sorted_files = map { $_->[0] } @files_sorted_by_mtime;
演算子を連結することにより、中間配列に変数の宣言は必要ありません。
@sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, (stat($_))[8] ] } glob('*');
grep を挿入して、ソートする前にリストをフィルタリングすることもできます(同じキャッシュ値でフィルタリングする場合):
例(過去24時間に変更されたファイルのリストは、最終変更時刻でソートされています):
@sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } grep { $_->[1] > (time - 24 * 3600 } map { [ $_, (stat($_))[8] ] } glob('*');
map関数は、リストの各要素に対して式を実行し、リストの結果を返します。次のリストがあったとしましょう
@names = ("andrew", "bob", "carol" );
そしてこれらの名前の最初の文字を大文字にしたかった。それらをループして各要素のucfirstを呼び出すことも、次のようにすることもできます
@names = map (ucfirst, @names);
map関数は、関数型プログラミングパラダイムのアイデアです。関数型プログラミングでは、関数はファーストクラスのオブジェクトです。つまり、他の関数に引数として渡すことができます。マップはシンプルですが、これの非常に便利な例です。引数(f
と呼ぶ)とリストl
を引数として取ります。 map
は1つの引数を取る関数である必要があり、mapは単に<=>をリスト<=>のすべての要素に適用します。 <=>は、すべての要素に対して必要なことをすべて実行できます。すべての要素に1を追加、すべての要素を2乗、データベースにすべての要素を書き込み、すべての要素に対してWebブラウザーウィンドウを開きます。 p>
<=>を使用する利点は、リストの要素の繰り返しをうまくカプセル化できることです。あなたがしなければならないことは、すべての要素に対して<!> quot; do <=>と言うことです。そして、それを行う最善の方法を決定するのは<=>次第です。たとえば、<=>は、複数のスレッド間で作業を分割するために実装でき、呼び出し元に対して完全に透過的です。
注、<=>はPerl固有のものではありません。関数型言語で使用される標準的な手法です。関数ポインターを使用してCで実装することも、<!> quot;関数オブジェクト<!> quot;を使用してC ++で実装することもできます。
<!> quot;ジャストシュガー<!> quot;厳しいです。ループは砂糖にすぎないことを忘れないでください-ifとgotoはループ構造が行うことすべてを行うことができます。
マップは、頭の中ではるかに複雑な操作を保持するのに役立つ高度な機能であるため、より大きな問題をコーディングおよびデバッグできます。
言い換えると<!> quot;効果的なPerlプログラミング<!> quot;ホール<!> amp;シュワルツ、 マップは悪用される可能性がありますが、既存のリストから新しいリストを作成するのに最適だと思います。
3、2、<!> ampの正方形のリストを作成します。 1:
@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;
パスワードの生成:
$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G
mapを使用してリストを変換し、結果を別のリストに割り当てます。grepを使用してリストをフィルター処理し、結果を別のリストに割り当てます。 <!> quot; other <!> quot; listは、変換/フィルタリングしているリストと同じ変数にすることができます。
my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";
既存のリストから新しいリストを作成したいときにいつでも使用されます。
たとえば、文字列のリストに解析関数をマップして、それらを整数に変換できます。
リストを statements ではなく expression として変換できます。次のように定義された兵士のハッシュを想像してください:
{ name => 'John Smith'
, rank => 'Lieutenant'
, serial_number => '382-293937-20'
};
その後、名前のリストを個別に操作できます。
たとえば、
map { $_->{name} } values %soldiers
は式です。式が許可されている場所であればどこにでも移動できます。ただし、式に割り当てることはできません。
${[ sort map { $_->{name} } values %soldiers ]}[-1]
配列のインデックスを作成し、最大値を取得します。
my %soldiers_by_sn = map { $->{serial_number} => $_ } values %soldiers;
操作式の利点の1つは、一時変数に起因するバグを削減できることです。
マッコイ氏が検討のためにすべてのハットフィールドを除外したい場合、最小限のコーディングでそのチェックを追加できます。
my %soldiers_by_sn
= map { $->{serial_number}, $_ }
grep { $_->{name} !~ m/Hatfield$/ }
values %soldiers
;
これらの式のチェーンを続けて、特定の目的のためにこのデータとのやり取りを深くしなければならない場合に、もっと多くのことをするふりをする多くのコードを書く必要はありません。
他の人が言ったように、mapはリストからリストを作成します。 <!> quot; mapping <!> quot;を考えてください。あるリストの内容を別のリストに入れます。特許番号のリストを取得し、特許出願へのハイパーリンクを印刷するCGIプログラムのコードを次に示します。
my @patents = ('7,120,721', '6,809,505', '7,194,673');
print join(", ", map { "<a href=\"http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL&p=1&u=/netahtml/srchnum.htm&r=0&f=S&l=50&TERM1=$_\">$_</a>" } @patents);
他の人が言ったように、マップはリストの変換に最も役立ちます。言及されていないのは、mapと<!> quot; equivalent <!> quot;の違いです。 forループ。
1つの違いは、繰り返しを行うリストを変更する式ではforがうまく機能しないことです。これらの1つは終了し、もう1つは終了しません。
perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'
別の小さな違いは、マップブロック内の context はリストコンテキストですが、forループはvoidコンテキストを与えることです。