Chinaunix

标题: sort,map的特別用法... [打印本页]

作者: apile    时间: 2003-08-07 14:09
标题: sort,map的特別用法...
下面這種用法不常見,但是很多高手都會用這類方法,所以才會有很多人說
他看不懂,Perl在幹嘛...
1.
這是把passwd裡面的內容按照user id大小來排序...
如果要反向排序只要把$a與$b對調即可。
open PWD "/etc/passwd";
@passwd = <PWD>;;
@key = map { (split /:/)[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>;;

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

[ 本帖最后由 apile 于 2008-5-4 13:05 编辑 ]
作者: 白水    时间: 2003-08-07 14:19
标题: sort,map的特別用法...
我晕...一个也看不懂....怎么办啊??
作者: chsz20    时间: 2003-08-07 14:33
标题: sort,map的特別用法...
比 C 简单好多呀
作者: powerplane    时间: 2003-08-07 15:38
标题: sort,map的特別用法...
偶只有第一条借助perldoc免强看懂,其它都不懂噢
-M是什么意思?
作者: apile    时间: 2003-08-07 15:49
标题: sort,map的特別用法...
我從Effective Perl Programming這本書看到的...
要看的話一定要從最右邊開始看...
其實不難,但是很多perl的精華都在裡面了..

各位可以當成練功..
討論討論...
真的沒人知道,
  
下星期我再解釋上面的用法,是怎麼回事...
作者: ocean2000    时间: 2003-08-07 18:25
标题: sort,map的特別用法...
见过这种用法哦,-M就是根据modify参数来sort

apile 老大真实厉害,好好向你学习
作者: deathcult    时间: 2003-08-08 16:14
标题: sort,map的特別用法...
spilt印错了 :)
作者: bytewolf    时间: 2003-08-09 16:51
标题: sort,map的特別用法...

  1. #!/usr/bin/perl

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

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

复制代码


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

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

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

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

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

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


  15. 然后sort {$a->;[1] <=>; $b->;[1] }便是根据@_中的每项(实际上是数组引用)的第二部分uid的大小来排序,这里的sort排序是标准的方法,很多书里都有例子。
  16. 现在,经过上面的过程,数组@_中的内容已经以uid大小排好序了, 现在我们只要按@_排好的顺序读取每一项内容的第一部分,以
  17. ['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的例子,简单却很好的完成了任务,你可以看看:

  1. #!/usr/bin/perl

  2. use strict;

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

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

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

  18. my $sortby;

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

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

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

复制代码


请指教^-^
作者: apile    时间: 2003-08-09 17:49
标题: sort,map的特別用法...
沒錯...bytewolf 很厲害..
呵呵...其實這些就是在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
标题: sort,map的特別用法...
http://www.effectiveperl.com/recipes/sorting.html
作者: bytewolf    时间: 2003-08-09 22:15
标题: sort,map的特別用法...
[quote]原帖由 "lgjut"]http://www.effectiveperl.com/recipes/sorting.html[/quote 发表:
     

呵呵~~,写得很清楚
作者: powerplane    时间: 2003-08-10 11:58
标题: sort,map的特別用法...
偶想问一个问题,是不是用一个sort/map就会产生一个新的数组?如果是这样,下面的程序产生了4个数组吧?

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

作者: powerplane    时间: 2003-08-10 12:19
标题: sort,map的特別用法...
偶明白了,的确会产生数组。不过由于最右面的map用了"[]",所以只是一个reference,所以copy的工作量不会很大哦!
作者: bytewolf    时间: 2003-08-10 15:22
标题: sort,map的特別用法...
[quote]原帖由 "powerplane"][/quote 发表:
     


不是不是~~你可以认为只对这一个缺省的数组@_进行操作,没有产生4个数组
作者: powerplane    时间: 2003-08-11 20:11
标题: sort,map的特別用法...
哦,那么在"="出现的地方会产生数组复制吗?
作者: apile    时间: 2003-08-12 18:12
标题: sort,map的特別用法...
事實上有兩個anonymous array在那裡面動作
只是我們只用他的ref,
第一個是map{[$_,-M]}@files;
這兒..第二個是sort{$a->;[1]<=>;$b->;[1]}
最後map{$_->;[0]}則是排序完後的新的array..
至於copy數量..當然不少..
光sort就copy很多次了..
map也有..

作者: powerplane    时间: 2003-08-12 19:46
标题: sort,map的特別用法...
呵呵,我的意思是:
copy的数量多,但是copy的工作负荷不大,因为都是copy指针,不是整个字符串。如果字符。
作者: dahe_1984    时间: 2013-01-17 17:32
好帖如此之多。精华贴需要耐心看,比看书涨的快。
作者: rubyish    时间: 2013-01-18 11:53
哦,偶明白了,的确写得很清楚.

作者: jiang870320    时间: 2013-01-22 14:23
cat passwd| perl -e 'print map{$_->[0]}sort{$a->[1]<=>$b->[1]}map{[$_,(split /:/)[2]]}<>'




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2