没有真正得到地图功能的重点.任何人都可以用例子解释它的用途吗
使用它而不是循环是否有任何性能优势,还是只是糖?
任何时候您想要基于另一个列表生成列表:
# 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);
(编辑:修复了最后一行中缺少的"键%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 {'"' . $_ . '"' } @_ }
另请参阅Schwartzian变换以获得地图的高级用法.
它对于查找哈希也很方便:
my %is_boolean = map { $_ => 1 } qw(true false);
相当于
my %is_boolean = ( true => 1, false => 1 );
那里没有多少积蓄,但假设你想定义%is_US_state
?
map用于通过转换另一个列表的元素来创建列表.
grep用于通过过滤另一个列表的元素来创建列表.
sort用于通过对另一个列表的元素进行排序来创建列表.
这些运算符中的每一个都接收代码块(或表达式),用于转换,过滤或比较列表的元素.
对于map,块的结果将成为新列表中的一个(或多个)元素.当前元素别名为$ _.
对于grep,块的布尔结果决定是否将原始列表的元素复制到新列表中.当前元素别名为$ _.
对于sort,块接收两个元素(别名为$ a和$ b),并且预期返回-1,0或1中的一个,指示$ a是大于,等于还是小于$ b.
所述的Schwartzian变换使用这些运营商能够有效的缓存值(属性),以在一个分拣列表中使用,尤其是当计算这些性能具有一个非平凡成本.
它的工作原理是创建一个中间数组,该数组具有作为元素数组引用的原始元素和我们想要排序的计算值.这个数组被传递给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('*');
map函数在列表的每个元素上运行一个表达式,并返回列表结果.可以说我有以下列表
@names = ("andrew", "bob", "carol" );
我想把这些名字的第一个字母大写.我可以遍历它们并调用每个元素的ucfirst,或者我可以执行以下操作
@names = map (ucfirst, @names);
map函数是函数式编程范例的一个概念.在函数式编程中,函数是第一类对象,这意味着它们可以作为参数传递给其他函数.地图是一个简单但非常有用的例子.它将一个函数(让我们称之为f
)和一个列表作为其参数l
. f
必须是一个带有一个参数的函数,map只适用f
于列表的每个元素l
. f
可以对每个元素执行任何操作:向每个元素添加一个元素,为每个元素添加方块,将每个元素写入数据库,或者为每个元素打开Web浏览器窗口,这恰好是一个有效的URL.
使用的优点map
是它很好地封装了迭代列表的元素.你所要做的就是说" f
对每一个元素都做,并且map
决定如何最好地做到这一点.例如map
可以实现在多个线程之间分割它的工作,并且它对调用者来说是完全透明的.
注意,这map
完全不是Perl特有的.它是功能语言使用的标准技术.它甚至可以使用函数指针在C中实现,或者在使用"函数对象"的C++中实现.
"只是糖"是苛刻的.记住,循环只是糖 - 如果和goto可以做所有循环结构做的事情.
Map是一个足够高级别的功能,它可以帮助您在头脑中执行更复杂的操作,因此您可以编写和调试更大的问题.