マップ機能のポイントを実際に取得していません。誰かがその使用例を説明できますか?
ループの代わりにこれを使用するとパフォーマンス上の利点はありますか、それとも単なる砂糖ですか?
マップ機能のポイントを実際に取得していません。誰かがその使用例を説明できますか?
ループの代わりにこれを使用するとパフォーマンス上の利点はありますか、それとも単なる砂糖ですか?
別のリストに基づいてリストを生成したいときはいつでも:
# 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);
(編集:最後の行にない「keys%params」を修正しました)
この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 {'"' . $_ . '"' } @_ }
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の場合、ブロックは ($a と $b にエイリアスされた) 2 つの要素を受け取り、$a が $b より大きいか、等しいか、または小さいかを示す -1、0、または 1 のいずれかを返すことが期待されます。
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 関数は、関数型プログラミング パラダイムからのアイデアです。関数型プログラミングでは、関数は第一級オブジェクトです。つまり、他の関数に引数として渡すことができます。Map は単純ですが、これの非常に便利な例です。引数として関数 ( let call it f
) と listを取りますl
。 f
は引数を 1 つ取る関数でなければならず、 map は単純f
に list のすべての要素に適用されますl
。 f
すべての要素に対して必要なことは何でも行うことができます: すべての要素に 1 を追加する、すべての要素を四角形にする、すべての要素をデータベースに書き込む、またはすべての要素に対して Web ブラウザー ウィンドウを開くことができますが、これはたまたま有効な URL です。
を使用する利点はmap
、リストの要素の反復処理を適切にカプセル化することです。あなたがしなければならないことは、「f
すべての要素に対して行う」と言うだけであり、それmap
を行う最善の方法を決定するのはあなた次第ですmap
.
map
これは Perl に固有のものではないことに注意してください。これは、関数型言語で使用される標準的な手法です。関数ポインタを使用して C で実装することも、「関数オブジェクト」を使用して C++ で実装することもできます。
map 関数は、リストの各要素に対して式を実行し、リストの結果を返します。次のリストがあるとしましょう
@names = ("andrew", "bob", "carol" );
そして、これらの名前の最初の文字を大文字にしたかったのです。それらをループして各要素の ucfirst を呼び出すか、次のようにすることができます
@names = map (ucfirst, @names);
「ただの砂糖」は厳しいです。ループは単なる糖衣に過ぎないことを思い出してください。
Map は、より複雑な操作を頭の中で保持するのに役立つ十分に高レベルの関数であるため、より大きな問題をコーディングしてデバッグすることができます。
Hall&Schwartzによる「EffectivePerl Programming」を言い換えると、マップは悪用される可能性がありますが、既存のリストから新しいリストを作成するために使用するのが最適だと思います。
3、2、および1の平方のリストを作成します。
@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;
リストをステートメントではなく式として変換できます。次のように定義された兵士のハッシュを想像してください。
{ 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 を使用してリストを変換し、結果を別のリストに割り当て、grep を使用してリストをフィルタリングし、結果を別のリストに割り当てます。「その他」のリストは、変換/フィルタリングしているリストと同じ変数にすることができます。
my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";
パスワードの生成:
$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G
既存のリストから新しいリストを作成したいときにいつでも使用できます。
たとえば、文字列のリストに解析関数をマップして、それらを整数に変換できます。
他の人が言ったように、 map はリストを変換するのに最も便利です。言及されていないのは、map と「同等の」for ループの違いです。
1 つの違いは、for は繰り返し処理するリストを変更する式ではうまく機能しないことです。これらの 1 つは終了し、もう 1 つは終了しません。
perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'
もう 1 つの小さな違いは、マップ ブロック内のコンテキストはリスト コンテキストですが、for ループは無効なコンテキストを与えることです。
他の人が言ったように、 map はリストからリストを作成します。あるリストの内容を別のリストに「マッピング」することを考えてみてください。以下は、特許番号のリストを取得し、特許出願へのハイパーリンクを出力する 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);