简体   繁体   English

Perl 的地图有什么意义?

[英]What's the point of Perl's map?

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?使用它代替循环是否有任何性能优势,或者只是糖?

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.出于可读性目的,有些人可能更喜欢冗长的循环代码,但就个人而言,我发现map更具可读性。

First of all, it's a simple way of transforming an array: rather than saying eg首先,这是一种转换数组的简单方法:而不是说例如

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 eg它对于构建快速查找表也很有用:而不是例如

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, eg 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) (编辑:修复了最后一行中缺失的“keys %params”)

The map function is used to transform lists. map函数用于转换列表。 It's basically syntactic sugar for replacing certain types of for[each] loops.它基本上是用于替换某些类型的for[each]循环的语法糖。 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 {'"' . $_ . '"' } @_ }

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

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 ?那里没有太多节省,但假设您想定义%is_US_state

map is used to create a list by transforming the elements of another list. map用于通过转换另一个列表的元素来创建列表。

grep is used to create a list by filtering elements of another list. grep用于通过过滤另一个列表的元素来创建列表。

sort is used to create a list by sorting the elements of another list. sort用于通过对另一个列表的元素进行排序来创建一个列表。

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.对于map ,块的结果成为新列表中的一个(或多个)元素。 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.对于grep ,块的布尔结果决定是否将原始列表的元素复制到新列表中。 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.对于sort ,该块接收两个元素(别名为 $a 和 $b)并预期返回 -1、0 或 1 之一,指示 $a 是大于、等于还是小于 $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. Schwartzian 变换使用这些运算符有效地缓存要用于对列表进行排序的值(属性),尤其是在计算这些属性具有非平凡成本时。

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).这个数组被传递给 sort,它比较已经计算的值,创建另一个中间数组(这个数组已经排序),它又被传递给另一个映射,该映射丢弃缓存的值,从而将数组恢复到它的初始列表元素(但是现在按所需的顺序)。

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):您还可以在排序之前通过插入grep过滤列表(如果您想过滤相同的缓存值):

Example (a list of the files modified in the last 24 hours sorted the last modification time):示例(最近 24 小时内修改的文件列表按最后修改时间排序):

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

The map function is an idea from the functional programming paradigm. map 函数是函数式编程范式中的一个想法。 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. Map 是一个简单但非常有用的例子。 It takes as its arguments a function (lets call it f ) and a list l .它接受一个函数(我们称之为f )和一个列表l作为它的参数。 f has to be a function taking one argument, and map simply applies f to every element of the list l . f必须是一个带一个参数的函数,而 map 只是将f应用于列表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. f可以对每个元素执行您需要执行的任何操作:向每个元素添加一个,对每个元素进行平方,将每个元素写入数据库,或者为每个元素打开一个 Web 浏览器窗口,这恰好是一个有效的 URL。

The advantage of using map is that it nicely encapsulates iterating over the elements of the list.使用map的优点是它很好地封装了对列表元素的迭代。 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.你所要做的就是说“对每个元素都做f ,这取决于map决定如何最好地做到这一点。例如, map可能被实现以在多个线程之间拆分它的工作,并且它对呼叫者,召集者。

Note, that map is not at all specific to Perl.请注意,该map根本不是特定于 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".它甚至可以使用函数指针在 C 中实现,或者使用“函数对象”在 C++ 中实现。

The map function runs an expression on each element of a list, and returns the list results. map 函数对列表的每个元素运行一个表达式,并返回列表结果。 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我可以遍历它们并调用每个元素的 ucfirst,或者我可以执行以下操作

@names = map (ucfirst, @names);

"Just sugar" is harsh. “只是糖”是苛刻的。 Remember, a loop is just sugar -- if's and goto can do everything loop constructs do and more.请记住,循环只是糖—— if 和 goto 可以完成循环结构所能做的所有事情,甚至更多。

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. Map 是一个足够高级的函数,它可以帮助您在头脑中处理更复杂的操作,因此您可以编写和调试更大的问题。

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.套用 Hall & Schwartz 的“Effective Perl Programming”的话,map 可能会被滥用,但我认为它最好用于从现有列表创建新列表。

Create a list of the squares of 3,2, & 1:创建一个包含 3,2, & 1 的平方的列表:

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

Generate password:生成密码:

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

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.您使用 map 转换列表并将结果分配给另一个列表,使用 grep 过滤列表并将结果分配给另一个列表。 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";

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.如果 McCoy 先生想过滤掉所有的 Hatfields 以供考虑,您可以使用最少的编码添加该检查。

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.我可以继续链接这些表达式,这样如果我与这​​些数据的交互必须深入到特定目的,我就不必编写大量代码来假装我要做更多。

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.例如,您可以在字符串列表上映射解析函数以将它们转换为整数。

As others have said, map creates lists from lists.正如其他人所说, map 从列表中创建列表。 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:这是来自 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 is most useful for transforming a list.正如其他人所说, map 对于转换列表最有用。 What hasn't been mentioned is the difference between map and an "equivalent" for loop.没有提到的是 map 和“等效” for 循环之间的区别。

One difference is that for doesn't work well for an expression that modifies the list its iterating over.一个区别是 for 不适用于修改其迭代列表的表达式。 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.另一个小区别是 map 块内的上下文是一个列表上下文,但 for 循环赋予了一个 void 上下文。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM