首页 > 学技术 > 技术网文 > Perl > 正文

[精华] sort,map的特別用法...


来源 chinaunix.net kuqin整理

下面這種用法不常見,但是很多高手都會用這類方法,所以才會有很多人說
他看不懂,Perl在幹嘛...
1.
這是把passwd裡面的內容按照user id大小來排序...
如果要反向排序只要把$a與$b對調即可。
open PWD "/etc/passwd";
@passwd = <PWD>;;
@key = map { ([color=red]split[/color] /:/)[2] } @passwd;
@by_uid =@passwd[sort { $key[$a] <=>; $key[$b] } 0..$#key];

2.Oricish Maneuver:很奇怪的名稱
 @sorted = sort {-M $a <=>;-M $b } @files; 依照modified時間來排序
但是-M使用得太頻繁了,所以改良為
@sorted = sort { 
  ($m{$a} ||= -M $a) <=>;($m{$b} ||=-M $b)
} @files;
因為||=與hash的關係,所以使用-M的次數會少很多,有n個element就只使用n次.. 原本的-M $a <=>;-M $b比較沒有效率..

3.Schwartizian Transform:
@sorted_name = map {$_->;[0] }# 取出原本的filename
    sort { $a->;[1] <=>; $b->;[1]  }#--依照modified_time去比大小
    map { [$_,-M] } #--[filename,modified_time]
   @files;      #----輸入的所有filename

open(PWD,"/etc/passwd");
@by_uid = 
   map { $_->;[0]}
   sort {$a->;[1] <=>; $b->;[1] }
   map { [$_,(split /:/)[2]] }
  <PWD>;;

是不是很神奇...最近正在練習第三種技巧...:)



 白水 回复于:2003-08-07 14:19:21

我晕...一个也看不懂....怎么办啊??


 chsz20 回复于:2003-08-07 14:33:19

比 C 简单好多呀


 powerplane 回复于:2003-08-07 15:38:34

偶只有第一条借助perldoc免强看懂,其它都不懂噢
-M是什么意思?


 apile 回复于:2003-08-07 15:49:26

我從Effective Perl Programming這本書看到的...
要看的話一定要從最右邊開始看...
其實不難,但是很多perl的精華都在裡面了..

各位可以當成練功..:)
討論討論...
真的沒人知道,
  
下星期我再解釋上面的用法,是怎麼回事...


 ocean2000 回复于:2003-08-07 18:25:56

见过这种用法哦,-M就是根据modify参数来sort

apile 老大真实厉害,好好向你学习


 deathcult 回复于:2003-08-08 16:14:48

spilt印错了 :)


 bytewolf 回复于:2003-08-09 16:51:22


#!/usr/bin/perl

@by_uid = map { $_->;[0]} sort {$a->;[1] <=>; $b->;[1] } map { [$_,(split /:/)[2]] } <DATA>;; 
for(@by_uid){print ."\n";}

__DATA__
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
mailnull:x:47:47::/var/spool/mqueue:/dev/null
rpm:x:37:37::/var/lib/rpm:/bin/bash
ident:x:98:98:pident user:/:/sbin/nologin
pcap:x:77:77::/var/arpwatch:/sbin/nologin
sshd:x:501:501::/dev/null:/dev/null
mysql:x:100:101:MySQL server:/var/lib/mysql:/bin/bash
cnhackTNT:x:0:0::/home/cnhackTNT:/bin/bash
chutium:x:504:504:Seraph Chutium:/home/chutium/:/bin/bash




@by_uid = map { $_->;[0]} sort {$a->;[1] <=>; $b->;[1] } map { [$_,(split /:/)[2]] } <DATA>;; 

以下对上面的根据uid来排序的code做出解释:

<DATA>;实际上可以看成一个数组,内容就是__DATA__下的那些,这里我们把它当作是@DATA这个数组(Apile原文里就是/etc下的passwd文件的内容)。

map{[$_,(split /:/)[2]}从@DATA中取出每条数据,并对每条数据以 : 作为分割符分割,也就是 split(/:/,$_);

然后返回一个数组@_,以sshd:x:501:501::/dev/null:/dev/null为例,那么这个返回的数组中的一项可以看成:
$_=['sshd:x:501:501::/dev/null:/dev/null','501'];
从上可看出这个$_是一个匿名的数组引用,他实际上包含两个部分,其中第二部分是uid,也就是$_->;[1]
现在@_中的内容变成了:

....前面的省略.....
['rpm:x:37:37::/var/lib/rpm:/bin/bash','37']
['ident:x:98:98:pident user:/:/sbin/nologin,'98']
['pcap:x:77:77::/var/arpwatch:/sbin/nologin','77']
['sshd:x:501:501::/dev/null:/dev/null','501']
....后面的省略.....


然后sort {$a->;[1] <=>; $b->;[1] }便是根据@_中的每项(实际上是数组引用)的第二部分uid的大小来排序,这里的sort排序是标准的方法,很多书里都有例子。
现在,经过上面的过程,数组@_中的内容已经以uid大小排好序了, 现在我们只要按@_排好的顺序读取每一项内容的第一部分,以
['sshd:x:501:501::/dev/null:/dev/null','501']为例,我们要取他的第一部分'sshd:x:501:501::/dev/null:/dev/null',也就是$_->;[0],并将其保存到数组@by_uid中就好了,而map { $_->;[0]}很好的完成了这项工作。 




实际上只要明掌握了map,sort的用法就能容易的理解上面算法的意思

下面是perlmonks.com上的一个sort的例子,简单却很好的完成了任务,你可以看看:

#!/usr/bin/perl

use strict;

use vars qw(@results);
@results = ("Bean Burrito|0.69|5",
            "Seven-Layer Burrito|1.39|8",
            "Pintos -n- cheese|0.59|0",
);

sub numerically_by_item
{
  my($which)=@_;
  return sub { (split(/\|/,$a))[$which] <=>; (split(/\|/,$b))[$which] }
}

sub alpha_by_item
{
  my($which)=@_;
  return sub { (split(/\|/,$a))[$which] cmp (split(/\|/,$b))[$which] }
}

my $sortby;

print "Sorted by price\n";
$sortby = numerically_by_item(1);
print join("\n",sort $sortby @results);
print "\n\n";

print "Sorted by name\n";
$sortby = alpha_by_item(0);
print join("\n",sort $sortby @results);
print "\n\n";

print "Sorted by messiness\n";
$sortby = numerically_by_item(2);
print join("\n",sort $sortby @results);
print "\n\n";



请指教^-^


 apile 回复于:2003-08-09 17:49:57

沒錯...bytewolf 很厲害.. :D 
呵呵...其實這些就是在perl裡面最常見到的reference and slice array hash
一個用C寫幾十行的程序,在Perl裡面這樣短短兩三行就完成了..^_^
要看懂這類的代碼,要從最後面開始看起...看似複雜..其實不難哩..
另外有人說看不懂-M那個...解釋如下:

sort{-M $a<=>; -M $b} @files
其實這裡$a,$b你可以看程式@files裡面一個一個檔案名稱..
如果要你寫sorting的程式..通常我們都會用
for($i=0;$i<@array;$i++){
  for($j=$i+1;$j<@array;$j++){
}
}
這樣兩個loop去做..已計算-M(檔案的modified time)的次數來說..
總共要算2XnX(n-1)X(n-2)X..X2X1
他的複雜度O(sqrt(n,2))就是n的2次方..
如果n越多..那麼程式就會越慢..
利用$m{$_}這個hash將檔案名稱與modified timesave下來,
因為key是unique的..且配合 $a||$b的特性,如果$a或$b是真會回傳$a
或$b的數值、而不是true or false... 這樣子...就只需要算n次就好了...
這在計算的效能上會提升很多..$a ||= $b; 等於$a=$a||$b;
另外雖然hash計算速率上會比array慢,但在複雜度上從2次方變成1次方..
因此計算的速率仍然能變得很快...

所以有時候是我們的邏輯讓Perl變慢的..倒不一定是他本身就慢..:)


 lgjut 回复于:2003-08-09 21:30:07

http://www.effectiveperl.com/recipes/sorting.html


 bytewolf 回复于:2003-08-09 22:15:10

引用:原帖由 "lgjut"]http://www.effectiveperl.com/recipes/sorting.html
 发表:

     

呵呵~~,写得很清楚


 powerplane 回复于:2003-08-10 11:58:06

偶想问一个问题,是不是用一个sort/map就会产生一个新的数组?如果是这样,下面的程序产生了4个数组吧?

3.Schwartizian Transform: 
@sorted_name = map {$_->;[0] }# 取出原本的filename,产生第三个数组,结构为[filename]。赋值过程中,把该数组copy到@sorted_name中,实际产生了第四个数组吧? 
sort { $a->;[1] <=>; $b->;[1] }#--产生第二个数组(结构跟第一个数组一样,并且,依照modified_time去比大小) 
map { [$_,-M] } #--产生第一个数组,[filename,modified_time] 
@files; #---- 



 powerplane 回复于:2003-08-10 12:19:07

偶明白了,的确会产生数组。不过由于最右面的map用了"[]",所以只是一个reference,所以copy的工作量不会很大哦!


 bytewolf 回复于:2003-08-10 15:22:28

引用:原帖由 "powerplane"]
 发表:

     


不是不是~~你可以认为只对这一个缺省的数组@_进行操作,没有产生4个数组


 powerplane 回复于:2003-08-11 20:11:06

哦,那么在"="出现的地方会产生数组复制吗?


 apile 回复于:2003-08-12 18:12:21

事實上有兩個anonymous array在那裡面動作
只是我們只用他的ref,
第一個是map{[$_,-M]}@files;
這兒..第二個是sort{$a->;[1]<=>;$b->;[1]}
最後map{$_->;[0]}則是排序完後的新的array..
至於copy數量..當然不少..:)
光sort就copy很多次了..
map也有..
:)


 powerplane 回复于:2003-08-12 19:46:42

呵呵,我的意思是:
copy的数量多,但是copy的工作负荷不大,因为都是copy指针,不是整个字符串。如果字符。




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=133419
转载请注明作者名及原文出处



收藏本页到: