版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址

  http://www.cnblogs.com/Colin-Cai/p/7087128.html 

  作者:窗户

  QQ:6679072

  E-mail:6679072@qq.com

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

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写递归的更多相关文章

  1. awk的递归

    想来惭愧,之前写的一篇文章<用awk写递归>里多少是传递里错误的信息.虽然那篇文章目的上是为了给出一种思路,但实际上awk是可以支持函数局部变量的. awk对于局部变量的支持比起大多数过程 ...

  2. iOS:Block写递归

    首先来一个 oc 的递归: - (int)sum:(int)num { ) { return num; } ]; } 写递归算法只需要记住两点即可: 1. 有一个明确的出口 2. 不满足条件出口时,自 ...

  3. 分享一个的c++写的,模仿awk的框架类CAwkDoc

    这是我好多年前,模仿awk写的. awk大家都比较熟悉,使用awk处理文件,读取文件,分割字段这些工作awk自己帮你实现了. 程序员只要编写业务逻辑代码,并且awk还提供了很多常用的字符串操作函数,可 ...

  4. awk 手册--【转载】

    1. 前言 有关本手册 : 这是一本awk学习指引,  其重点着重于 : l         awk 适于解决哪些问题 ? l         awk 常见的解题模式为何 ? 为使读者快速掌握awk解 ...

  5. awk中文手册

    1. 前言 有关本手册 : 这是一本awk学习指引, 其重点着重于 : l        awk 适于解决哪些问题 ? l        awk 常见的解题模式为何 ? 为使读者快速掌握awk解题的模 ...

  6. 见过的最好AWK手册

    原文: http://linuxfire.com.cn/~lily/awk.html 简体中文版由bones7456 (http://li2z.cn)整理. 原文:应该是 http://phi.sin ...

  7. awk 手册

    1. 前言 有关本手册 : 这是一本awk学习指引, 其重点着重于 : l        awk 适于解决哪些问题 ? l        awk 常见的解题模式为何 ? 为使读者快速掌握awk解题的模 ...

  8. [转]awk使用手册

    awk 手册 简体中文版由bones7456 (bones7456@gmail.com)整理. 原文:应该是 http://phi.sinica.edu.tw/aspac/reports/94/940 ...

  9. awk手册

    awk 手册 简体中文版由bones7456 (bones7456@gmail.com)整理. 原文:应该是 http://phi.sinica.edu.tw/aspac/reports/94/940 ...

随机推荐

  1. MarkDown本地图片上传工具制作总结

    引言:开始尝试使用MarkDown语法写文档,发现图片必须用外链的形式才能插入到文章中,而自己平时最常用的插入图片方式就是QQ截屏,觉得很不方便所以制作的小工具辅助上传,因为时间和水平有限,其实代码写 ...

  2. linux上安装tcl

    1. 首先下载安装包,推荐下载activetcl(对tcl源码进行了预编译,安装步骤简单).打开网址http://activestate.com找到activetcl的社区版(社区版是免费的,找到li ...

  3. 区块链入门(2):搭建以太坊私有链(private network of ethereum),以及挖矿的操作..

    在做一些测试工作的时候, 为了方便控制以及更快的进入真正的测试工作,可能需要搭建一个私有的以太坊网络. 而以太坊节点之间能够互相链接需要满足1)相同的协议版本2)相同的networkid,所以搭建私有 ...

  4. 解决window.navigator.geolocation.getCurrentPosition在IOS10系统中无法进行地理定位问题

    昨天接到用户通知说在点击"看场地"时无法获取地理位置信息. 在接到通知时,首先想到的是排查机型问题.由于客户多为IOS用户,所以最先看的是在安卓是有没有此问题的发生,调查结果为安卓 ...

  5. flask-login ----系统权限设计部分小结

    tips: 事实证明.开发是一项苦力活.但是代码只有自己写的才是令人感到放心的.不过仅仅是从开发角度来说.从维护和安全角度来说,当然还是引入模块比较爽 但是引入的模块总会有一些问题.碰到的最大问题就是 ...

  6. Hibernate原理

    Hibernate使用基本上会,但是却一直不知道Hibernate内部是怎么工作的 什么是Hibernate? Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象 ...

  7. error C2039: 'SetDefaultDllDirectories'错误解决办法

    使用VS2013+WDK8.1+Win7开发UMDF驱动,当使用了CComPtr类,包含了atlcomcli.h头文件却报错,错误如下: Error 3 error C2039: 'SetDefaul ...

  8. Javascript开发技巧(JS中的变量、运算符、分支结构、循环结构)

    一.Js简介和入门 继续跟进JS开发的相关教程. <!-- [使用JS的三种方式] 1.HTML标签中内嵌JS(不提倡使用): 示例:<button onclick="javas ...

  9. javaWeb学习总结(3)- Servlet总结(servlet的主要接口、类)

    Servlet总结01——servlet的主要接口.类 (一)servlet类 Servlet主要类.接口的结构如下图所示: 要编写一个Servlet需要实现javax.servlet.Servlet ...

  10. 20个php框架

    对于Web开发者来说,PHP是一款非常强大而又受欢迎的编程语言.世界上很多顶级的网站都是基于PHP开发的.本文我们来回顾一下20个2014年最优秀的PHP框架. 每一个开发者都知道,拥有一个强大的框架 ...