用awk写递归

看到自己很多年前写的一篇帖子,觉得有些意义,转录过来,稍加修改。

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);
}

  

时间: 2024-08-09 10:36:08

用awk写递归的相关文章

awk的递归

版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/9082551.html 作者:窗户 QQ:6679072 E-mail:[email protected] 想来惭愧,之前写的一篇文章<用awk写递归>里多少是传递里错误的信息.虽然那篇文章目的上是为了给出一种思路,但实际上awk是可以支持函数局部变量的. awk对于局部变量的支持比起大多数过程式语言来说很是怪异,它只在函数的参数里支

awk写shell分发脚本

使用hadoop或者部署hadoop的时候,经常会遇到要往其他机器上拷贝文件的情况,但是,如果单个写的话,碰巧集群又很大,会累死的,现在使用一种awk写shell脚本,速度很快 命令 awk 'BEGIN{ print "#! /bin/sh";for(i=10;i<=200;i++) print "scp -r hadoop 10.6.8."i":/usr/hadoop" }'  > scp.sh 这样就可以轻松分发10.6.8.1

iOS:Block写递归

首先来一个 oc 的递归: - (int)sum:(int)num { if (num == 0) { return num; } return num + [self sum:num - 1]; } 写递归算法只需要记住两点即可: 1. 有一个明确的出口 2. 不满足条件出口时,自己调用自己 按照以上思路用 block 改写一下: static int (^sumBlock)(int) = ^ (int num) { if (num == 0) { return num; } return n

手写递归

题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 程序分析:兔子的规律为数列1,1,2,3,5,8,13,21.... 具体分析如下: f(1) = 1(第1个月有一对兔子)f(2) = 1(第2个月还是一对兔子)f(3) = 2(原来有一对兔子,第3个开始,每个月生一对兔子)f(4) = 3(原来有两对兔子,有一对可以生育)f(5) = 5(原来有3对兔子,第3个月出生的那对兔子也可以生育了,那

从汉诺塔上写递归

需求: 求出每一级的数量,但是父级+子级的所有数量. 比如 第一级本身数量为1 而子级是2 ,那么相加就是3,但是2级还有3级,就需要再加上3级.依次类推到最后一级数量之和就是这个分类的总数量. 实现的思路: 1:我们需要找到自身的数量. 2.1:我们找到自身的下一级所有数量相加 ,返回数量. 2.2:子级再去调用方法1,这样最后累加. 代码: class Test { public static function getAllData() { echo '<pre>'; $data=[];

怎样写出一个递归程序

作为小白,我看到递归程序只是能看懂,但是自己写不出来,我知道要有一个临界条件(这个并不难找),但我不知道怎么演进,这让我十分头疼,因此找到了一篇个人认为写的不错的文章如下,根据我对递归的理解和疑问对原文做了一些标注,欢迎各位大佬,写下自己对递归的理解,本小白感激不尽. 如何写一个递归程序 总是听到大大们说递归递归的,自己写程序的时候却用不到递归.其中的原因,一个是害怕写递归,另一个就是不知道什么时候用递归.这篇文章就浅析一下,希望看完之后不再害怕递归,这就是本文最大的目的. 递归到底有什么意义?

awk中文手册

1. 前言 有关本手册 : 这是一本awk学习指引, 其重点着重于 : l        awk 适于解决哪些问题 ? l        awk 常见的解题模式为何 ? 为使读者快速掌握awk解题的模式及特性, 本手册系由一些较具代表性的范例及其题解所构成; 各范例由浅入深, 彼此间相互连贯,范例中并对所使用的awk语法及指令辅以必要的说明. 有关awk的指令, 函数,...等条列式的说明则收录于附录中, 以利读者往后撰写程序时查阅. 如此编排, 可让读者在短时间内顺畅地学会使用awk来解决问题

awk手册

awk 手册 简体中文版由bones7456 ([email protected])整理. 原文:应该是 http://phi.sinica.edu.tw/aspac/reports/94/94011/ 但是原文很乱. 说明:之前也是对awk几乎一无所知,无意中看到这篇文章,网上一搜,居然没有像样的简体中文版.有的也是不怎么完整,或者错误一大堆的.于是就顺手整理了下这篇文章.通过整理这篇文章,自己也渐渐掌握了awk的种种用法. 原文可能比较老,有些目前已经不适用的命令有所改动,文中所有命令均在u

递归的一些应用(一)遍历文件夹

函数的递归调用 递归的含义 递归其实也只是一种算法上的描述,不是一种新的语法! 有时候,我们解决问题的时候,会遇到这种情况,当我们把一个大的问题按照某种解决方案分成若干个小的问题的时候,发现这些小问题的解决方案其实和刚才大问题的解决方案又是一样的! 典型的,比如:求阶乘! 10! = 10 * 9! 9! =  9 * 8! 8! = 8 * 7! …… 语法上,函数的递归调用,就是函数在执行的过程中自己又调用自己! 递归的两个要点: 1,  递归的出口:就是指什么时候停止递归调用 2,  递归