📄 arrays.html
字号:
22 if [ -z "$1" ] # 没有可压入的数据项? 23 then 24 return 25 fi 26 27 let "SP -= 1" # 更新栈指针. 28 stack[$SP]=$1 29 30 return 31 } 32 33 pop() # 从栈中弹出数据项. 34 { 35 Data= # 清空保存数据项的中间变量. 36 37 if [ "$SP" -eq "$BP" ] # 栈空? 38 then 39 return 40 fi # 这使得SP不会超过100, 41 #+ 例如, 这可以防止堆栈失控. 42 43 Data=${stack[$SP]} 44 let "SP += 1" # 更新栈指针. 45 return 46 } 47 48 status_report() # 打印当前状态. 49 { 50 echo "-------------------------------------" 51 echo "REPORT" 52 echo "Stack Pointer = $SP" 53 echo "Just popped \""$Data"\" off the stack." 54 echo "-------------------------------------" 55 echo 56 } 57 58 59 # ======================================================= 60 # 现在, 来点乐子. 61 62 echo 63 64 # 看你是否能从空栈里弹出数据项来. 65 pop 66 status_report 67 68 echo 69 70 push garbage 71 pop 72 status_report # 压入garbage, 弹出garbage. 73 74 value1=23; push $value1 75 value2=skidoo; push $value2 76 value3=FINAL; push $value3 77 78 pop # FINAL 79 status_report 80 pop # skidoo 81 status_report 82 pop # 23 83 status_report # 后进, 先出! 84 85 # 注意: 栈指针在压栈时减, 86 #+ 在弹出时加. 87 88 echo 89 90 exit 0 91 92 # ======================================================= 93 94 95 # 练习: 96 # ----- 97 98 # 1) 修改"push()"函数, 99 # + 使其调用一次就能够压入多个数据项. 100 101 # 2) 修改"pop()"函数, 102 # + 使其调用一次就能弹出多个数据项. 103 104 # 3) 给那些有临界操作的函数添加出错检查. 105 # 说明白一些, 就是让这些函数返回错误码, 106 # + 返回的错误码依赖于操作是否成功完成, 107 # + 如果没有成功完成, 那么就需要启动合适的处理动作. 108 109 # 4) 以这个脚本为基础, 110 # + 编写一个用栈实现的四则运算计算器. </PRE></FONT></TD></TR></TABLE><HR></DIV><P>--</P><P>如果想对数组<SPANCLASS="QUOTE">"下标"</SPAN>做一些比较诡异的操作, 可能需要使用中间变量. 对于那些有这种需求的项目来说, 还是应该考虑使用功能更加强大的编程语言, 比如Perl或C. </P><DIVCLASS="EXAMPLE"><HR><ANAME="QFUNCTION"></A><P><B>例子 26-15. 复杂的数组应用: <EM>探索一个神秘的数学序列</EM></B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # Douglas Hofstadter的声名狼藉的序列"Q-series": 4 5 # Q(1) = Q(2) = 1 6 # Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), 当n>2时 7 8 # 这是一个令人感到陌生的, 没有规律的"乱序"整数序列. 9 # 序列的头20项, 如下所示: 10 # 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12 11 12 # 请参考相关书籍, Hofstadter的, "_Goedel, Escher, Bach: An Eternal Golden Braid_", 13 #+ 第137页. 14 15 16 LIMIT=100 # 需要计算的数列长度. 17 LINEWIDTH=20 # 每行打印的个数. 18 19 Q[1]=1 # 数列的头两项都为1. 20 Q[2]=1 21 22 echo 23 echo "Q-series [$LIMIT terms]:" 24 echo -n "${Q[1]} " # 输出数列头两项. 25 echo -n "${Q[2]} " 26 27 for ((n=3; n <= $LIMIT; n++)) # C风格的循环条件. 28 do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] 当n>2时 29 # 需要将表达式拆开, 分步计算, 30 #+ 因为Bash不能够很好的处理复杂数组的算术运算. 31 32 let "n1 = $n - 1" # n-1 33 let "n2 = $n - 2" # n-2 34 35 t0=`expr $n - ${Q[n1]}` # n - Q[n-1] 36 t1=`expr $n - ${Q[n2]}` # n - Q[n-2] 37 38 T0=${Q[t0]} # Q[n - Q[n-1]] 39 T1=${Q[t1]} # Q[n - Q[n-2]] 40 41 Q[n]=`expr $T0 + $T1` # Q[n - Q[n-1]] + Q[n - Q[n-2]] 42 echo -n "${Q[n]} " 43 44 if [ `expr $n % $LINEWIDTH` -eq 0 ] # 格式化输出. 45 then # ^ 取模操作 46 echo # 把每行都拆为20个数字的小块. 47 fi 48 49 done 50 51 echo 52 53 exit 0 54 55 # 这是Q-series的一个迭代实现. 56 # 更直接明了的实现是使用递归, 请读者作为练习完成. 57 # 警告: 使用递归的方法来计算这个数列的话, 会花费非常长的时间. </PRE></FONT></TD></TR></TABLE><HR></DIV><P>--</P><P>Bash仅仅支持一维数组, 但是我们可以使用一个小手段, 这样就可以模拟多维数组了. </P><DIVCLASS="EXAMPLE"><HR><ANAME="TWODIM"></A><P><B>例子 26-16. 模拟一个二维数组, 并使他倾斜</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # twodim.sh: 模拟一个二维数组. 3 4 # 一维数组由单行组成. 5 # 二维数组由连续的多行组成. 6 7 Rows=5 8 Columns=5 9 # 5 X 5 的数组. 10 11 declare -a alpha # char alpha [Rows] [Columns]; 12 # 没必要声明. 为什么? 13 14 load_alpha () 15 { 16 local rc=0 17 local index 18 19 for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y 20 do # 你可以随你的心意, 使用任意符号. 21 local row=`expr $rc / $Columns` 22 local column=`expr $rc % $Rows` 23 let "index = $row * $Rows + $column" 24 alpha[$index]=$i 25 # alpha[$row][$column] 26 let "rc += 1" 27 done 28 29 # 更简单的方法: 30 #+ declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y ) 31 #+ 但是如果写的话, 就缺乏二维数组的"风味"了. 32 } 33 34 print_alpha () 35 { 36 local row=0 37 local index 38 39 echo 40 41 while [ "$row" -lt "$Rows" ] # 以"行序为主"进行打印: 42 do #+ 行号不变(外层循环), 43 #+ 列号进行增长. (译者注: 就是按行打印) 44 local column=0 45 46 echo -n " " # 按照行方向打印"正方形"数组. 47 48 while [ "$column" -lt "$Columns" ] 49 do 50 let "index = $row * $Rows + $column" 51 echo -n "${alpha[index]} " # alpha[$row][$column] 52 let "column += 1" 53 done 54 55 let "row += 1" 56 echo 57 58 done 59 60 # 更简单的等价写法为: 61 # echo ${alpha[*]} | xargs -n $Columns 62 63 echo 64 } 65 66 filter () # 过滤掉负的数组下标. 67 { 68 69 echo -n " " # 产生倾斜. 70 # 解释一下, 这是怎么做到的. 71 72 if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]] 73 then 74 let "index = $1 * $Rows + $2" 75 # 现在, 按照旋转方向进行打印. 76 echo -n " ${alpha[index]}" 77 # alpha[$row][$column] 78 fi 79 80 } 81 82 83 84 85 rotate () # 将数组旋转45度 -- 86 { #+ 从左下角进行"平衡". 87 local row 88 local column 89 90 for (( row = Rows; row > -Rows; row-- )) 91 do # 反向步进数组, 为什么? 92 93 for (( column = 0; column < Columns; column++ )) 94 do 95 96 if [ "$row" -ge 0 ] 97 then 98 let "t1 = $column - $row" 99 let "t2 = $column"100 else101 let "t1 = $column"102 let "t2 = $column + $row"103 fi 104 105 filter $t1 $t2 # 将负的数组下标过滤出来. 106 # 如果你不做这一步, 将会怎样? 107 done108 109 echo; echo110 111 done 112 113 # 数组旋转的灵感来源于Herbert Mayer所著的114 #+ "Advanced C Programming on the IBM PC"的例子(第143-146页)115 #+ (参见参考书目). 116 # 由此可见, C语言能够做到的好多事情, 117 #+ 用shell脚本一样能够做到. 118 119 }120 121 122 #--------------- 现在, 让我们开始吧. ------------#123 load_alpha # 加载数组. 124 print_alpha # 打印数组. 125 rotate # 逆时钟旋转45度打印. 126 #-----------------------------------------------------#127 128 exit 0129 130 # 这是有点做作, 不是那么优雅. 131 132 # 练习:133 # -----134 # 1) 重新实现数组加载和打印函数, 135 # 让其更直观, 可读性更强. 136 #137 # 2) 详细地描述旋转函数的原理. 138 # 提示: 思考一下倒序索引数组的实现. 139 #140 # 3) 重写这个脚本, 扩展它, 让不仅仅能够支持非正方形的数组. 141 # 比如6 X 4的数组. 142 # 尝试一下, 在数组旋转时, 做到最小"失真". </PRE></FONT></TD></TR></TABLE><HR></DIV><P>二维数组本质上其实就是一个一维数组, 只不过是添加了<EM>行</EM>和<EM>列</EM>的寻址方式, 来引用和操作数组的元素而已. </P><P>这里有一个精心制作的模拟二维数组的例子, 请参考<AHREF="contributed-scripts.html#LIFESLOW">例子 A-10</A>. </P><P>--</P><P>还有两个使用脚本的更有趣的例子, 请参考: <P></P><UL><LI><P><AHREF="commandsub.html#AGRAM2">例子 14-3</A>和<AHREF="contributed-scripts.html#HASHEX2">例子 A-23</A></P></LI></UL> </P></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="list-cons.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html"ACCESSKEY="H">首页</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="devproc.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">列表结构</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="part4.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">/dev和/proc</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -