1. 程式人生 > >用awk寫遞歸

用awk寫遞歸

ron 一個棧 fun 例子 自然 int 可能 tran 為什麽

看到自己很多年前寫的一篇帖子,覺得有些意義,轉錄過來,稍加修改。

awk是一種腳本語言,語法接近C語言,我比較喜歡用,gawk甚至可以支持tcp/ip,用起來非常方便。

awk也支持遞歸,只是awk不支持局部變量,所有的變量都是全局的,於是寫遞歸有些麻煩。本文說白了,也只是借awk說一種編程的思路罷了。

原文如下:

awk支持函數,也支持遞歸。但awk並不支持局部變量,於是看上去遞歸函數很不好實現,因為在某一級調用函數的時候,裏面的變量在該級調用還沒有退出前就可能會被別的調用給修改掉,於是得到的結果會與期望並不一致。
我們考慮C語言,它的局部變量放在硬件支持的棧(一般用棧指針)內。於是我們就去思考,為什麽是棧呢?我們來考慮一個具體的函數調用順序:

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的遞歸函數就可以出來了。
以下是用遞歸來算一個數組中的最大值(每遞歸一級就把數組分為兩段,每段求最大值),只是舉一個例子,可以擴展到任意應用。

#!/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寫遞歸