Perl 的地图有什么意义?

发布于 2024-07-05 19:23:27 字数 71 浏览 11 评论 0原文

没有真正理解地图功能的要点。 谁能用例子解释它的用途?

使用它代替循环是否有任何性能优势,或者它只是糖?

Not really getting the point of the map function. Can anyone explain with examples its use?

Are there any performance benefits to using this instead of a loop or is it just sugar?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

夕色琉璃 2024-07-12 19:23:27

任何时候你想生成一个基于另一个列表的列表:

# 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 更具可读性。

Any time you want to generate a list based another list:

# Double all elements of a list
my @double = map { $_ * 2 } (1,2,3,4,5);
# @double = (2,4,6,8,10);

Since lists are easily converted pairwise into hashes, if you want a hash table for objects based on a particular attribute:

# @user_objects is a list of objects having a unique_id() method
my %users = map { $_->unique_id() => $_ } @user_objects;
# %users = ( $id => $obj, $id => $obj, ...);

It's a really general purpose tool, you have to just start using it to find good uses in your applications.

Some might prefer verbose looping code for readability purposes, but personally, I find map more readable.

眼泪也成诗 2024-07-12 19:23:27

首先,这是一种转换数组的简单方法:而不是说例如,

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”)

First of all, it's a simple way of transforming an array: rather than saying e.g.

my @raw_values = (...);
my @derived_values;
for my $value (@raw_values) {
    push (@derived_values, _derived_value($value));
}

you can say

my @raw_values = (...);
my @derived_values = map { _derived_value($_) } @raw_values;

It's also useful for building up a quick lookup table: rather than e.g.

my $sentence = "...";
my @stopwords = (...);
my @foundstopwords;
for my $word (split(/\s+/, $sentence)) {
    for my $stopword (@stopwords) {
       if ($word eq $stopword) {
           push (@foundstopwords, $word);
       }
    }
}

you could say

my $sentence = "...";
my @stopwords = (...);
my %is_stopword = map { $_ => 1 } @stopwords;
my @foundstopwords = grep { $is_stopword{$_} } split(/\s+/, $sentence);

It's also useful if you want to derive one list from another, but don't particularly need to have a temporary variable cluttering up the place, e.g. rather than

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);

you say the much simpler

my %params = ( username => '...', password => '...', action => $action );
my $url = $ENV{SCRIPT_NAME} . '?'
    . join('&', map { $_ . '=' . CGI::escape($params{$_}) } keys %params);

(Edit: fixed the missing "keys %params" in that last line)

蓝颜夕 2024-07-12 19:23:27

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 {'"' . $_ . '"' } @_ }

The map function is used to transform lists. It's basically syntactic sugar for replacing certain types of for[each] loops. Once you wrap your head around it, you'll see uses for it everywhere:

my @uppercase = map { uc } @lowercase;
my @hex       = map { sprintf "0x%x", $_ } @decimal;
my %hash      = map { $_ => 1 } @array;
sub join_csv { join ',', map {'"' . $_ . '"' } @_ }
三生殊途 2024-07-12 19:23:27

另请参阅Schwartzian 变换以了解地图的高级用法。

See also the Schwartzian transform for advanced usage of map.

佞臣 2024-07-12 19:23:27

它对于创建查找哈希也很方便:

my %is_boolean = map { $_ => 1 } qw(true false);

相当于

my %is_boolean = ( true => 1, false => 1 );

There's not much saving there, but假设您想定义 %is_US_state

It's also handy for making lookup hashes:

my %is_boolean = map { $_ => 1 } qw(true false);

is equivalent to

my %is_boolean = ( true => 1, false => 1 );

There's not much savings there, but suppose you wanted to define %is_US_state?

心安伴我暖 2024-07-12 19:23:27

ma​​p 用于通过转换另一个列表的元素来创建列表。

grep 用于通过过滤另一个列表的元素来创建列表。

sort 用于通过对另一个列表的元素进行排序来创建列表。

这些运算符中的每一个都接收一个代码块(或表达式),用于转换、过滤或比较列表的元素。

对于ma​​p,块的结果成为新列表中的一个(或多个)元素。 当前元素的别名为 $_。

对于grep,块的布尔结果决定是否将原始列表的元素复制到新列表中。 当前元素的别名为 $_。

对于 sort,该块接收两个元素(别名为 $a 和 $b),并预计返回 -1、0 或 1 之一,指示 $a 是否大于、等于或小于 $ b.

Schwartzian 变换 使用这些运算符来有效地缓存用于对列表进行排序的值(属性) ,特别是当计算这些属性的成本不菲时。

它的工作原理是创建一个中间数组,其中元素数组引用原始元素和我们要排序的计算值。 该数组被传递给 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 is used to create a list by transforming the elements of another list.

grep is used to create a list by filtering elements of another list.

sort is used to create a list by sorting the elements of another list.

Each of these operators receives a code block (or an expression) which is used to transform, filter or compare elements of the list.

For map, the result of the block becomes one (or more) element(s) in the new list. The current element is aliased to $_.

For grep, the boolean result of the block decides if the element of the original list will be copied into the new list. The current element is aliased to $_.

For sort, the block receives two elements (aliased to $a and $b) and is expected to return one of -1, 0 or 1, indicating whether $a is greater, equal or less than $b.

The Schwartzian Transform uses these operators to efficiently cache values (properties) to be used in sorting a list, especially when computing these properties has a non-trivial cost.

It works by creating an intermediate array which has as elements array references with the original element and the computed value by which we want to sort. This array is passed to sort, which compares the already computed values, creating another intermediate array (this one is sorted) which in turn is passed to another map which throws away the cached values, thus restoring the array to its initial list elements (but in the desired order now).

Example (creates a list of files in the current directory sorted by the time of their last modification):

@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;

By chaining the operators together, no declaration of variables is needed for the intermediate arrays;

@sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, (stat($_))[8] ] } glob('*');

You can also filter the list before sorting by inserting a grep (if you want to filter on the same cached value):

Example (a list of the files modified in the last 24 hours sorted the last modification time):

    @sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } grep { $_->[1] > (time - 24 * 3600 } map { [ $_, (stat($_))[8] ] } glob('*');
最佳男配角 2024-07-12 19:23:27

映射函数是函数式编程范式的一个想法。 在函数式编程中,函数是一等对象,这意味着它们可以作为参数传递给其他函数。 Map 是一个简单但非常有用的示例。 它以一个函数(我们称之为f)和一个列表l作为参数。 f 必须是一个带有一个参数的函数,而 map 只是将 f 应用于列表 l 的每个元素。 f 可以对每个元素执行您需要执行的任何操作:向每个元素添加一个、对每个元素求平方、将每个元素写入数据库,或者为每个元素打开一个 Web 浏览器窗口,这恰好是一个有效的网址。

使用map的优点是它很好地封装了对列表元素的迭代。 您所要做的就是对每个元素说“执行 f”,然后由 map 来决定如何最好地做到这一点。例如 map 可以实现将其工作分配给多个线程,并且对调用者来说是完全透明的。

请注意,map 根本不是 Perl 所特有的,它是 Perl 使用的标准技术。它甚至可以在 C 中使用函数指针来实现,或者在 C++ 中使用“函数对象”来实现。

The map function is an idea from the functional programming paradigm. In functional programming, functions are first-class objects, meaning that they can be passed as arguments to other functions. Map is a simple but a very useful example of this. It takes as its arguments a function (lets call it f) and a list l. f has to be a function taking one argument, and map simply applies f to every element of the list l. f can do whatever you need done to every element: add one to every element, square every element, write every element to a database, or open a web browser window for every element, which happens to be a valid URL.

The advantage of using map is that it nicely encapsulates iterating over the elements of the list. All you have to do is say "do f to every element, and it is up to map to decide how best to do that. For example map may be implemented to split up its work among multiple threads, and it would be totally transparent to the caller.

Note, that map is not at all specific to Perl. It is a standard technique used by functional languages. It can even be implemented in C using function pointers, or in C++ using "function objects".

梦途 2024-07-12 19:23:27

map 函数对列表的每个元素运行表达式,并返回列表结果。 假设我有以下列表

@names = ("andrew", "bob", "carol" );

,并且我想将每个名称的第一个字母大写。 我可以循环遍历它们并调用每个元素的 ucfirst,或者我可以执行以下操作

@names = map (ucfirst, @names);

The map function runs an expression on each element of a list, and returns the list results. Lets say I had the following list

@names = ("andrew", "bob", "carol" );

and I wanted to capitalize the first letter of each of these names. I could loop through them and call ucfirst of each element, or I could just do the following

@names = map (ucfirst, @names);
我是男神闪亮亮 2024-07-12 19:23:27

“只是糖”是严厉的。 请记住,循环只是糖——if 和 goto 可以完成循环结构所做的所有事情,甚至更多。

Map 是一个足够高级的函数,它可以帮助您在头脑中记住更复杂的操作,以便您可以编码和调试更大的问题。

"Just sugar" is harsh. Remember, a loop is just sugar -- if's and goto can do everything loop constructs do and more.

Map is a high enough level function that it helps you hold much more complex operations in your head, so you can code and debug bigger problems.

も让我眼熟你 2024-07-12 19:23:27

套用 Hall & 的《Effective Perl 编程》一文。 施瓦茨,
地图可能会被滥用,但我认为它最好用于从现有列表创建新列表。

创建 3,2, & 的平方列表 1:

@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;

To paraphrase "Effective Perl Programming" by Hall & Schwartz,
map can be abused, but I think that it's best used to create a new list from an existing list.

Create a list of the squares of 3,2, & 1:

@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;
简单气质女生网名 2024-07-12 19:23:27

生成密码:

$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G

Generate password:

$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G
与君绝 2024-07-12 19:23:27

您可以使用 map 来转换列表并将结果分配给另一个列表,使用 grep 来过滤列表并将结果分配给另一个列表。 “其他”列表可以是与您正在转换/过滤的列表相同的变量。

my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";

You use map to transform a list and assign the results to another list, grep to filter a list and assign the results to another list. The "other" list can be the same variable as the list you are transforming/filtering.

my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";
迷途知返 2024-07-12 19:23:27

它允许您将列表转换为表达式,而不是语句。 想象一下这样定义的士兵散列:

{ 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;

我发现操作表达式的优点之一是它减少了来自临时变量的错误。

如果麦考伊先生想要过滤掉所有哈特菲尔德以供考虑,您可以使用最少的编码添加该检查。

my %soldiers_by_sn 
    = map  { $->{serial_number}, $_ } 
      grep { $_->{name} !~ m/Hatfield$/ } 
      values %soldiers
      ;

我可以继续链接这些表达式,这样,如果我与这​​些数据的交互必须为了特定目的而深入,我就不必编写大量代码来假装我要做更多事情。

It allows you to transform a list as an expression rather than in statements. Imagine a hash of soldiers defined like so:

{ name          => 'John Smith'
, rank          => 'Lieutenant'
, serial_number => '382-293937-20'
};

then you can operate on the list of names separately.

For example,

map { $_->{name} } values %soldiers

is an expression. It can go anywhere an expression is allowed--except you can't assign to it.

${[ sort map { $_->{name} } values %soldiers ]}[-1]

indexes the array, taking the max.

my %soldiers_by_sn = map { $->{serial_number} => $_ } values %soldiers;

I find that one of the advantages of operational expressions is that it cuts down on the bugs that come from temporary variables.

If Mr. McCoy wants to filter out all the Hatfields for consideration, you can add that check with minimal coding.

my %soldiers_by_sn 
    = map  { $->{serial_number}, $_ } 
      grep { $_->{name} !~ m/Hatfield$/ } 
      values %soldiers
      ;

I can continue chaining these expression so that if my interaction with this data has to reach deep for a particular purpose, I don't have to write a lot of code that pretends I'm going to do a lot more.

夢归不見 2024-07-12 19:23:27

当您想要从现有列表创建新列表时,可以随时使用它。

例如,您可以将解析函数映射到字符串列表上,以将它们转换为整数。

It's used anytime you would like to create a new list from an existing list.

For instance you could map a parsing function on a list of strings to convert them to integers.

尛丟丟 2024-07-12 19:23:27

正如其他人所说,地图从列表创建列表。 考虑将一个列表的内容“映射”到另一个列表中。 以下是 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);

As others have said, map creates lists from lists. Think of "mapping" the contents of one list into another. Here's some code from a CGI program to take a list of patent numbers and print hyperlinks to the patent applications:

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);
2024-07-12 19:23:27

正如其他人所说,地图对于转换列表最有用。 没有提到的是 map 和“等效”for 循环之间的区别。

一个区别是 for 对于修改其迭代列表的表达式效果不佳。 其中一个终止,另一个则不终止:

perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'

另一个小区别是,map 块内的上下文是列表上下文,但 for 循环传递了一个 void 上下文。

As others have said, map is most useful for transforming a list. What hasn't been mentioned is the difference between map and an "equivalent" for loop.

One difference is that for doesn't work well for an expression that modifies the list its iterating over. One of these terminates, and the other doesn't:

perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'

Another small difference is that the context inside the map block is a list context, but the for loop imparts a void context.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文