并没有真正明白地图功能。任何人都可以用例子解释它的用途吗?
使用它而不是循环是否有任何性能优势,还是只是糖?
答案 0 :(得分:44)
任何时候你想根据另一个列表生成一个列表:
# 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
更具可读性。
答案 1 :(得分:28)
首先,它是一种转换数组的简单方法:而不是说例如。
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);
(编辑:修复了最后一行中缺少的“键%params”)
答案 2 :(得分:21)
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 {'"' . $_ . '"' } @_ }
答案 3 :(得分:18)
另请参阅Schwartzian transform了解地图的高级用法。
答案 4 :(得分:12)
使查找哈希值也很方便:
my %is_boolean = map { $_ => 1 } qw(true false);
相当于
my %is_boolean = ( true => 1, false => 1 );
那里没有太多的节省,但假设你想定义%is_US_state
?
答案 5 :(得分:11)
地图用于通过转换其他列表的元素来创建列表。
grep 用于通过过滤其他列表的元素来创建列表。
sort 用于通过对另一个列表的元素进行排序来创建列表。
这些运算符中的每一个都接收一个代码块(或表达式),用于转换,过滤或比较列表的元素。
对于 map ,块的结果将成为新列表中的一个(或多个)元素。当前元素别名为$ _。
对于 grep ,块的布尔结果决定是否将原始列表的元素复制到新列表中。当前元素别名为$ _。
对于排序,该块接收两个元素(别名为$ a和$ b),并且应返回-1,0或1中的一个,表示$ a是更大,相等还是不到$ b。
Schwartzian Transform使用这些运算符来有效地缓存用于排序列表的值(属性),尤其是在计算这些属性时具有非常重要的成本。
它的工作原理是创建一个中间数组,该数组具有作为元素数组引用的原始元素和我们想要排序的计算值。这个数组被传递给sort,它比较已经计算好的值,创建另一个中间数组(这个被排序),然后传递给另一个抛出缓存值的map,从而将数组恢复到它的初始列表元素(但是现在按照所需的顺序。)
示例(在当前目录中创建按上次修改时间排序的文件列表):
@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('*');
答案 6 :(得分:5)
map函数在列表的每个元素上运行一个表达式,并返回列表结果。假设我有以下列表
@names = ("andrew", "bob", "carol" );
我希望将每个名字的第一个字母大写。我可以遍历它们并调用每个元素的ucfirst,或者我可以执行以下操作
@names = map (ucfirst, @names);
答案 7 :(得分:5)
地图功能是功能编程范例的一个想法。在函数式编程中,函数是第一类对象,这意味着它们可以作为参数传递给其他函数。地图是一个简单但非常有用的例子。它将一个函数(让我们称之为f
)和一个列表l
作为其参数。 f
必须是一个带有一个参数的函数,而map只是将f
应用于列表l
的每个元素。 f
可以对每个元素执行任何操作:向每个元素添加一个元素,为每个元素添加方块,将每个元素写入数据库,或者为每个元素打开Web浏览器窗口,这恰好是有效的URL。
使用map
的优点在于它很好地封装了对列表元素的迭代。您所要做的就是对每个元素说“执行f
”,并由map
决定如何最好地执行此操作。例如,可以实施map
以分割其在多个线程之间工作,对调用者来说是完全透明的。
请注意,map
并非特定于Perl。它是功能语言使用的标准技术。它甚至可以使用函数指针在C中实现,或者在C ++中使用“函数对象”实现。
答案 8 :(得分:5)
“只是糖”是苛刻的。请记住,循环只是糖 - 如果和goto可以做所有循环结构做的事情。
Map是一个足够高级别的功能,它可以帮助您掌握更复杂的操作,因此您可以编写和调试更大的问题。
答案 9 :(得分:4)
用Hall&amp; amp;和“有效的Perl编程”来解释施瓦茨 地图可以被滥用,但我认为它最好用于从现有列表创建新列表。
创建3,2和2的正方形列表1:
@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;
答案 10 :(得分:4)
生成密码:
$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G
答案 11 :(得分:4)
使用map转换列表并将结果分配给另一个列表,grep过滤列表并将结果分配给另一个列表。 “其他”列表可以是与您正在转换/过滤的列表相同的变量。
my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";
答案 12 :(得分:3)
只要您想从现有列表中创建新列表,就可以使用它。
例如,您可以在字符串列表上映射解析函数,将它们转换为整数。
答案 13 :(得分:3)
它允许您将列表转换为表达式而不是语句。想象一下如此定义的士兵哈希:
{ 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;
我发现操作表达式的一个优点是它可以减少来自临时变量的错误。
如果McCoy先生想要过滤掉所有Hatfields以供考虑,您可以使用最少的编码添加该检查。
my %soldiers_by_sn
= map { $->{serial_number}, $_ }
grep { $_->{name} !~ m/Hatfield$/ }
values %soldiers
;
我可以继续链接这些表达式,这样如果我与这些数据的交互必须深入到特定目的,我就不必写很多假装我会做更多的代码。
答案 14 :(得分:1)
正如其他人所说,地图会从列表中创建列表。考虑将一个列表的内容“映射”到另一个列表中。以下是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);
答案 15 :(得分:1)
正如其他人所说,map对于转换列表最有用。没有提到的是map和“等效”for循环之间的区别。
一个区别在于,对于修改其迭代的列表的表达式不适用。其中一个终止,另一个不终止:
perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'
另一个小区别是地图块中的 context 是一个列表上下文,但for循环赋予了一个void上下文。