📄 arrays.html
字号:
><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # Optimized Sieve of Eratosthenes 3 # Script by Jared Martin, with very minor changes by ABS Guide author. 4 # Used in ABS Guide with permission (thanks!). 5 6 # Based on script in Advanced Bash Scripting Guide. 7 # http://tldp.org/LDP/abs/html/arrays.html#PRIMES0 (ex68.sh). 8 9 # http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf (reference) 10 # Check results against http://primes.utm.edu/lists/small/1000.txt 11 12 # Necessary but not sufficient would be, e.g., 13 # (($(sieve 7919 | wc -w) == 1000)) && echo "7919 is the 1000th prime" 14 15 UPPER_LIMIT=${1:?"Need an upper limit of primes to search."} 16 17 Primes=( '' $(seq ${UPPER_LIMIT}) ) 18 19 typeset -i i t 20 Primes[i=1]='' # 1 is not a prime. 21 until (( ( i += 1 ) > (${UPPER_LIMIT}/i) )) # Need check only ith-way. 22 do # Why? 23 if ((${Primes[t=i*(i-1), i]})) 24 # Obscure, but instructive, use of numeric eval in subscript. 25 then 26 until (( ( t += i ) > ${UPPER_LIMIT} )) 27 do Primes[t]=; done 28 fi 29 done 30 31 # echo ${Primes[*]} 32 echo # Change to original script for pretty-printing (80-col. display). 33 printf "%8d" ${Primes[*]} 34 echo; echo 35 36 exit $?</PRE></TD></TR></TABLE><HR></DIV><P>Compare these array-based prime number generators with an alternative that does not use arrays, <AHREF="contributed-scripts.html#PRIMES">Example A-16</A>.</P><P>--</P><P>Arrays lend themselves, to some extent, to emulating data structures for which Bash has no native support.</P><P><ANAME="STACKEX0"></A></P><DIVCLASS="EXAMPLE"><HR><ANAME="STACKEX"></A><P><B>Example 26-15. Emulating a push-down stack</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # stack.sh: push-down stack simulation 3 4 # Similar to the CPU stack, a push-down stack stores data items 5 #+ sequentially, but releases them in reverse order, last-in first-out. 6 7 BP=100 # Base Pointer of stack array. 8 # Begin at element 100. 9 10 SP=$BP # Stack Pointer. 11 # Initialize it to "base" (bottom) of stack. 12 13 Data= # Contents of stack location. 14 # Must use global variable, 15 #+ because of limitation on function return range. 16 17 declare -a stack 18 19 20 push() # Push item on stack. 21 { 22 if [ -z "$1" ] # Nothing to push? 23 then 24 return 25 fi 26 27 let "SP -= 1" # Bump stack pointer. 28 stack[$SP]=$1 29 30 return 31 } 32 33 pop() # Pop item off stack. 34 { 35 Data= # Empty out data item. 36 37 if [ "$SP" -eq "$BP" ] # Stack empty? 38 then 39 return 40 fi # This also keeps SP from getting past 100, 41 #+ i.e., prevents a runaway stack. 42 43 Data=${stack[$SP]} 44 let "SP += 1" # Bump stack pointer. 45 return 46 } 47 48 status_report() # Find out what's happening. 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 # Now, for some fun. 61 62 echo 63 64 # See if you can pop anything off empty stack. 65 pop 66 status_report 67 68 echo 69 70 push garbage 71 pop 72 status_report # Garbage in, garbage out. 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 # Last-in, first-out! 84 85 # Notice how the stack pointer decrements with each push, 86 #+ and increments with each pop. 87 88 echo 89 90 exit 0 91 92 # ======================================================= 93 94 95 # Exercises: 96 # --------- 97 98 # 1) Modify the "push()" function to permit pushing 99 # + multiple element on the stack with a single function call. 100 101 # 2) Modify the "pop()" function to permit popping 102 # + multiple element from the stack with a single function call. 103 104 # 3) Add error checking to the critical functions. 105 # That is, return an error code, depending on 106 # + successful or unsuccessful completion of the operation, 107 # + and take appropriate action. 108 109 # 4) Using this script as a starting point, 110 # + write a stack-based 4-function calculator.</PRE></TD></TR></TABLE><HR></DIV><P>--</P><P>Fancy manipulation of array <SPANCLASS="QUOTE">"subscripts"</SPAN> may require intermediate variables. For projects involving this, again consider using a more powerful programming language, such as Perl or C.</P><DIVCLASS="EXAMPLE"><HR><ANAME="QFUNCTION"></A><P><B>Example 26-16. Complex array application: <SPANCLASS="emphasis"><ICLASS="EMPHASIS">Exploring a weird mathematical series</I></SPAN></B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # Douglas Hofstadter's notorious "Q-series": 4 5 # Q(1) = Q(2) = 1 6 # Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), for n>2 7 8 # This is a "chaotic" integer series with strange 9 #+ and unpredictable behavior. 10 # The first 20 terms of the series are: 11 # 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12 12 13 # See Hofstadter's book, _Goedel, Escher, Bach: An Eternal Golden Braid_, 14 #+ p. 137, ff. 15 16 17 LIMIT=100 # Number of terms to calculate. 18 LINEWIDTH=20 # Number of terms printed per line. 19 20 Q[1]=1 # First two terms of series are 1. 21 Q[2]=1 22 23 echo 24 echo "Q-series [$LIMIT terms]:" 25 echo -n "${Q[1]} " # Output first two terms. 26 echo -n "${Q[2]} " 27 28 for ((n=3; n <= $LIMIT; n++)) # C-like loop expressions. 29 do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n>2 30 # Need to break the expression into intermediate terms, 31 #+ since Bash doesn't handle complex array arithmetic very well. 32 33 let "n1 = $n - 1" # n-1 34 let "n2 = $n - 2" # n-2 35 36 t0=`expr $n - ${Q[n1]}` # n - Q[n-1] 37 t1=`expr $n - ${Q[n2]}` # n - Q[n-2] 38 39 T0=${Q[t0]} # Q[n - Q[n-1]] 40 T1=${Q[t1]} # Q[n - Q[n-2]] 41 42 Q[n]=`expr $T0 + $T1` # Q[n - Q[n-1]] + Q[n - Q[n-2]] 43 echo -n "${Q[n]} " 44 45 if [ `expr $n % $LINEWIDTH` -eq 0 ] # Format output. 46 then # ^ modulo 47 echo # Break lines into neat chunks. 48 fi 49 50 done 51 52 echo 53 54 exit 0 55 56 # This is an iterative implementation of the Q-series. 57 # The more intuitive recursive implementation is left as an exercise. 58 # Warning: calculating this series recursively takes a VERY long time 59 #+ via a script. C/C++ would be more suitable.</PRE></TD></TR></TABLE><HR></DIV><P>--</P><P><ANAME="ARRAYMULTIDIM"></A></P><P>Bash supports only one-dimensional arrays, though a little trickery permits simulating multi-dimensional ones.</P><DIVCLASS="EXAMPLE"><HR><ANAME="TWODIM"></A><P><B>Example 26-17. Simulating a two-dimensional array, then tilting it</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # twodim.sh: Simulating a two-dimensional array. 3 4 # A one-dimensional array consists of a single row. 5 # A two-dimensional array stores rows sequentially. 6 7 Rows=5 8 Columns=5 9 # 5 X 5 Array. 10 11 declare -a alpha # char alpha [Rows] [Columns]; 12 # Unnecessary declaration. Why? 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 # Use different symbols if you like. 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 # Simpler would be 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 #+ but this somehow lacks the "flavor" of a two-dimensional array. 32 } 33 34 print_alpha () 35 { 36 local row=0 37 local index 38 39 echo 40 41 while [ "$row" -lt "$Rows" ] # Print out in "row major" order: 42 do #+ columns vary, 43 #+ while row (outer loop) remains the same. 44 local column=0 45 46 echo -n " " # Lines up "square" array with rotated one. 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 # The simpler equivalent is 61 # echo ${alpha[*]} | xargs -n $Columns 62 63 echo 64 } 65 66 filter () # Filter out negative array indices. 67 { 68 69 echo -n " " # Provides the tilt. 70 # Explain how. 71 72 if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]] 73 then 74 let "index = $1 * $Rows + $2" 75 # Now, print it rotated. 76 echo -n " ${alpha[index]}" 77 # alpha[$row][$column] 78 fi 79 80 } 81 82 83 84 85 rotate () # Rotate the array 45 degrees -- 86 { #+ "balance" it on its lower lefthand corner. 87 local row 88 local column 89 90 for (( row = Rows; row > -Rows; row-- )) 91 do # Step through the array backwards. Why? 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 else 101 let "t1 = $column" 102 let "t2 = $column + $row" 103 fi 104 105 filter $t1 $t2 # Filter out negative array indices. 106 # What happens if you don't do this? 107 done 108 109 echo; echo 110 111 done 112 113 # Array rotation inspired by examples (pp. 143-146) in 114 #+ "Advanced C Programming on the IBM PC," by Herbert Mayer 115 #+ (see bibliography). 116 # This just goes to show that much of what can be done in C 117 #+ can also be done in shell scripting. 118 119 } 120 121 122 #--------------- Now, let the show begin. ------------# 123 load_alpha # Load the array. 124 print_alpha # Print it out. 125 rotate # Rotate it 45 degrees counterclockwise. 126 #-----------------------------------------------------# 127 128 exit 0 129 130 # This is a rather contrived, not to mention inelegant simulation. 131 132 # Exercises: 133 # --------- 134 # 1) Rewrite the array loading and printing functions 135 # in a more intuitive and less kludgy fashion. 136 # 137 # 2) Figure out how the array rotation functions work. 138 # Hint: think about the implications of backwards-indexing an array. 139 # 140 # 3) Rewrite this script to handle a non-square array, 141 # such as a 6 X 4 one. 142 # Try to minimize "distortion" when the array is rotated.</PRE></TD></TR></TABLE><HR></DIV><P>A two-dimensional array is essentially equivalent to a one-dimensional one, but with additional addressing modes for referencing and manipulating the individual elements by <ICLASS="FIRSTTERM">row</I> and <ICLASS="FIRSTTERM">column</I> posi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -