用awk寫遞歸
阿新 • • 發佈:2017-06-27
ron 一個棧 fun 例子 自然 int 可能 tran 為什麽 f1調用f2;
f2調用f3;
f3返回;
f2調用f4;
f4調用f5;
f5返回;
f4返回;
f2返回;
f1返回;
按照這個循序,我們來思考每個函數開辟的棧空間:
f1的棧空間開辟(f1進棧)
f2的棧空間開辟(f2進棧)
f3的棧空間開辟(f3進棧)
f3的棧空間消亡(f3出棧)
f4的棧空間開辟(f4進棧)
f5的棧空間開辟(f5進棧)
f5的棧空間消亡(f5出棧)
f4的棧空間消亡(f4出棧)
f2的棧空間消亡(f2出棧)
f1的棧空間消亡(f1出棧)
原來跟我們數據結構裏的棧的先進後出是一回事情,所以叫棧。
而當前的我們取的變量的地址都是相對於棧指針來說的,這是相對,而不是像全局變量的那種絕對。
於是我們可以受到啟發,可以擴展這裏的棧指針和地址的概念,awk的遞歸函數就可以出來了。
以下是用遞歸來算一個數組中的最大值(每遞歸一級就把數組分為兩段,每段求最大值),只是舉一個例子,可以擴展到任意應用。
看到自己很多年前寫的一篇帖子,覺得有些意義,轉錄過來,稍加修改。
awk是一種腳本語言,語法接近C語言,我比較喜歡用,gawk甚至可以支持tcp/ip,用起來非常方便。
awk也支持遞歸,只是awk不支持局部變量,所有的變量都是全局的,於是寫遞歸有些麻煩。本文說白了,也只是借awk說一種編程的思路罷了。
原文如下:
awk支持函數,也支持遞歸。但awk並不支持局部變量,於是看上去遞歸函數很不好實現,因為在某一級調用函數的時候,裏面的變量在該級調用還沒有退出前就可能會被別的調用給修改掉,於是得到的結果會與期望並不一致。
我們考慮C語言,它的局部變量放在硬件支持的棧(一般用棧指針)內。於是我們就去思考,為什麽是棧呢?我們來考慮一個具體的函數調用順序:
f2調用f3;
f3返回;
f2調用f4;
f4調用f5;
f5返回;
f4返回;
f2返回;
f1返回;
按照這個循序,我們來思考每個函數開辟的棧空間:
f1的棧空間開辟(f1進棧)
f2的棧空間開辟(f2進棧)
f3的棧空間開辟(f3進棧)
f3的棧空間消亡(f3出棧)
f4的棧空間開辟(f4進棧)
f5的棧空間開辟(f5進棧)
f5的棧空間消亡(f5出棧)
f4的棧空間消亡(f4出棧)
f2的棧空間消亡(f2出棧)
f1的棧空間消亡(f1出棧)
原來跟我們數據結構裏的棧的先進後出是一回事情,所以叫棧。
而當前的我們取的變量的地址都是相對於棧指針來說的,這是相對,而不是像全局變量的那種絕對。
於是我們可以受到啟發,可以擴展這裏的棧指針和地址的概念,awk的遞歸函數就可以出來了。
#!/bin/awk -f func test1(a,start,len) { if(len<=1) return a[start]; x = test1(a,start,int(len/2)); y = test1(a,start+int(len/2),len-int(len/2)); return (x>y)?x:y; } func test2(a,start,len) { if(len<=1) return a[start]; testlen++; testa[testlen] = test2(a,start,int(len/2)); testlen++; testa[testlen] = test2(a,start+int(len/2),len-int(len/2)); testlen-=2; return (testa[testlen+1]>testa[testlen+2])?testa[testlen+1]:testa[testlen+2]; } func test3(a,start,len) { if(len<=1) { return a[start]; } V = V";"test3(a,start,int(len/2)); V = V";"test3(a,start+int(len/2),len-int(len/2)); xx = V; sub(/.*;/,"",xx); sub(/;[^;]+$/,"",V); yy = V; sub(/.*;/,"",yy); sub(/;[^;]+$/,"",V); return int(xx)>int(yy)?int(xx):int(yy); } NR==1{ way=$1; print $1 } NR==2{ max=$1; for(i=2;i<=NF;i++) if($i > max) max = $i; print max; for(i=1;i<=NF;i++) a[i] = $i; if(way == 2) print test2(a,1,NF); else if(way == 3) print test3(a,1,NF); else print test1(a,1,NF); exit(0); }
這裏面實現了三個遞歸函數,第一個是測試全局變量的汙染,它是得不到正確的答案的
第二個是用數組來模擬變量棧,testlen就是所謂的“棧頂指針”
第三個是用字符串來模擬變量棧,字符串末尾就是“棧頂指針”,每個“局部變量”之間是用分號隔開
用隨機數據測試一下這個應用:
linux-0gt0:/tmp/test # for((i=0;i<10;i++));do { echo $(($RANDOM % 3 + 1)); let count=$RANDOM%100+50; for((j=0;j<count;j++));do echo -n $(($RANDOM % 10000)) " "; done ; echo ; }|./1.awk ;done | sed -nr ‘N;N;p;/\n(.*)\n\1$/{s/.*/right\n/p;d;}; /^[23]/s/(.).*/\1 SO STRANGE!!!!!!!!!!!!!!!!!!!!!/p; s/.*/wrong\n/p‘ 2 9981 9981 right 3 9391 9391 right 1 9919 5257 wrong 2 9860 9860 right 3 9967 9967 right 3 9940 9940 right 3 9828 9828 right 2 9752 9752 right 3 9996 9996 right 2 9930 9930 right
當然,棧的數目自然也可以不只維系一個,test2和test3維系的是一個棧。
現在來實現test4和test5,兩個函數是test2和test3的變體,各自維系兩個棧。
#!/bin/awk -f #func test1(a,start,len) #{ # if(len<=1) # return a[start]; # x = test1(a,start,int(len/2)); # y = test1(a,start+int(len/2),len-int(len/2)); # return (x>y)?x:y; #} func test2(a,start,len) { if(len<=1) return a[start]; testlen++; testa[testlen] = test2(a,start,int(len/2)); testlen++; testa[testlen] = test2(a,start+int(len/2),len-int(len/2)); testlen-=2; return (testa[testlen+1]>testa[testlen+2])?testa[testlen+1]:testa[testlen+2]; } func test3(a,start,len) { if(len<=1) { return a[start]; } V = V";"test3(a,start,int(len/2)); V = V";"test3(a,start+int(len/2),len-int(len/2)); xx = V; sub(/.*;/,"",xx); sub(/;[^;]+$/,"",V); yy = V; sub(/.*;/,"",yy); sub(/;[^;]+$/,"",V); return int(xx)>int(yy)?int(xx):int(yy); } func test4(a,start,len) { if(len<=1) return a[start]; testlenx++; testleny++; testax[testlenx] = test4(a,start,int(len/2)); testay[testleny] = test4(a,start+int(len/2),len-int(len/2)); testlenx-=1; testleny-=1; return (testax[testlenx+1]>testay[testleny+1])?testax[testlenx+1]:testay[testleny+1]; } func test5(a,start,len) { if(len<=1) { return a[start]; } V1 = V1";"test5(a,start,int(len/2)); V2 = V2";"test5(a,start+int(len/2),len-int(len/2)); xx = V1; sub(/.*;/,"",xx); sub(/;[^;]+$/,"",V1); yy = V2; sub(/.*;/,"",yy); sub(/;[^;]+$/,"",V2); return int(xx)>int(yy)?int(xx):int(yy); } NR==1{ way=$1; print $1 } NR==2{ max=$1; for(i=2;i<=NF;i++) if($i > max) max = $i; print max; for(i=1;i<=NF;i++) a[i] = $i; if(way == 2) print test2(a,1,NF); else if(way == 3) print test3(a,1,NF); else if(way == 4) print test4(a,1,NF); else if(way == 5) print test5(a,1,NF); exit(0); }
測試一下,
linux-0gt0:/tmp/test # for((i=0;i<10;i++));do { echo $(($RANDOM % 2 + 4)); let count=$RANDOM%100+50; for((j=0;j<count;j++));do echo -n $(($RANDOM % 10000)) " "; done ; echo ; }|./1.awk ;done | sed -nr ‘N;N;p;/\n(.*)\n\1$/{s/.*/right\n/p;d;}; /^[234]/s/(.).*/\1 SO STRANGE!!!!!!!!!!!!!!!!!!!!!/p; s/.*/wrong\n/p‘ 5 9904 9904 right 4 9823 9823 right 5 9975 9975 right 4 9966 9966 right 5 9683 9683 right 5 9981 9981 right 4 9983 9983 right 5 9966 9966 right 5 9967 9967 right 5 9870 9870 right
當然,test4和test5各自維系的兩個棧其實差別和一個棧不大,因為兩個棧是同時進棧同時出棧的。
其實,即使兩個棧並非同時進出棧也是可以的,只是對於這裏的例子來說寫不出這麽復雜。
實際上,任意多的棧,任意進出棧,都是可以的。
這樣就可以做到更加靈活的應用。
軟件的擴展性比硬件強,我想,這就是軟件的用處吧。
還是這個取最大值,舉個含有多個棧,每個棧入出並不完全一致的例子,這裏的test6函數
#!/bin/awk -f func test1(a,start,len) { if(len<=1) return a[start]; x = test1(a,start,int(len/2)); y = test1(a,start+int(len/2),len-int(len/2)); return (x>y)?x:y; } func test2(a,start,len) { if(len<=1) return a[start]; testlen++; testa[testlen] = test2(a,start,int(len/2)); testlen++; testa[testlen] = test2(a,start+int(len/2),len-int(len/2)); testlen-=2; return (testa[testlen+1]>testa[testlen+2])?testa[testlen+1]:testa[testlen+2]; } func test3(a,start,len) { if(len<=1) { return a[start]; } V = V";"test3(a,start,int(len/2)); V = V";"test3(a,start+int(len/2),len-int(len/2)); xx = V; sub(/.*;/,"",xx); sub(/;[^;]+$/,"",V); yy = V; sub(/.*;/,"",yy); sub(/;[^;]+$/,"",V); return int(xx)>int(yy)?int(xx):int(yy); } func test4(a,start,len) { if(len<=1) return a[start]; testlenx++; testleny++; testax[testlenx] = test4(a,start,int(len/2)); testay[testleny] = test4(a,start+int(len/2),len-int(len/2)); testlenx-=1; testleny-=1; return (testax[testlenx+1]>testay[testleny+1])?testax[testlenx+1]:testay[testleny+1]; } func test5(a,start,len) { if(len<=1) { return a[start]; } V1 = V1";"test5(a,start,int(len/2)); V2 = V2";"test5(a,start+int(len/2),len-int(len/2)); xx = V1; sub(/.*;/,"",xx); sub(/;[^;]+$/,"",V1); yy = V2; sub(/.*;/,"",yy); sub(/;[^;]+$/,"",V2); return int(xx)>int(yy)?int(xx):int(yy); } func test6(a,start,len) { if(len <= 1) { return a[start]; } else if(len == 2) { return (a[start]>a[start+1])?a[start]:a[start+1]; } else if(len == 3) { var1 = (a[start]>a[start+1])?a[start]:a[start+1]; return (var1>a[start+2])?var1:a[start+2]; } var2 = int(rand()*10000)%3+2; if(var2 == 2) { testlenx++; testleny++; testax[testlenx] = test6(a,start,int(len/2)); testay[testleny] = test6(a,start+int(len/2),len-int(len/2)); testlenx-=1; testleny-=1; return (testax[testlenx+1]>testay[testleny+1])?testax[testlenx+1]:testay[testleny+1]; } else if(var2 == 3) { testlenx++; testleny++; testlenz++; testax[testlenx] = test6(a,start,int(len/3)); testay[testleny] = test6(a,start+int(len/3),int(len/3)); testaz[testlenz] = test6(a,start+2*int(len/3),len-2*int(len/3)); testlenx-=1; testleny-=1; testlenz-=1; var1 = (testax[testlenx+1]>testay[testleny+1])?testax[testlenx+1]:testay[testleny+1]; return ((var1>testaz[testlenz+1])?var1:testaz[testlenz+1]); } else if(var2 == 4) { testlenx++; testleny++; testlenz++; testlenA++; testax[testlenx] = test6(a,start,int(len/4)); testay[testleny] = test6(a,start+int(len/4),int(len/4)); testaz[testlenz] = test6(a,start+2*int(len/4),int(len/4)); testaA[testlenA] = test6(a,start+3*int(len/4),len-3*int(len/4)); testlenx-=1; testleny-=1; testlenz-=1; testlenA-=1; var1 = (testax[testlenx+1]>testay[testleny+1])?testax[testlenx+1]:testay[testleny+1]; var4 = (testaz[testlenz+1]>testaA[testlenA+1])?testaz[testlenz+1]:testaA[testlenA+1]; return ((var1>var4)?var1:var4); } } BEGIN { srand(systime()); } NR==1{ way=$1; print $1 } NR==2{ max=$1; for(i=2;i<=NF;i++) if($i > max) max = $i; print max; for(i=1;i<=NF;i++) a[i] = $i; if(way == 2) print test2(a,1,NF); else if(way == 3) print test3(a,1,NF); else if(way == 4) print test4(a,1,NF); else if(way == 5) print test5(a,1,NF); else if(way == 6) print test6(a,1,NF); else print test1(a,1,NF); exit(0); }
用awk寫遞歸