📄 ch09_01.htm
字号:
$row = $AoA[$i]; for $j ( 0 .. $#{$row} ) { print "element $i $j is $row->[$j]\n"; }}</pre></blockquote></p><h3 class="sect2">9.1.4. Slices</h3><a name="INDEX-2097"></a><p><a name="INDEX-2098"></a><a name="INDEX-2099"></a>If you want to access a slice (part of a row) of a multidimensionalarray, you're going to have to do some fancy subscripting. Thepointer arrows give us a nice way to access a single element, but nosuch convenience exists for slices. You can always extract theelements of your slice one-by-one with a loop:<blockquote><pre class="programlisting">@part = ();for ($y = 7; $y < 13; $y++) { push @part, $AoA[4][$y];}</pre></blockquote>This particular loop could be replaced with an array slice:<blockquote><pre class="programlisting">@part = @{ $AoA[4] } [ 7..12 ];</pre></blockquote>If you want a <em class="emphasis">two-dimensional slice</em>, say, with <tt class="literal">$x</tt> running from<tt class="literal">4..8</tt> and <tt class="literal">$y</tt> from <tt class="literal">7..12</tt>, here's one way to do it:<blockquote><pre class="programlisting">@newAoA = ();for ($startx = $x = 4; $x <= 8; $x++) { for ($starty = $y = 7; $y <= 12; $y++) { $newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y]; }}</pre></blockquote><a name="INDEX-2100"></a>In this example, the individual values within our destinationtwo-dimensional array, <tt class="literal">@newAoA</tt>, are assigned one by one, taken from atwo-dimensional subarray of <tt class="literal">@AoA</tt>. An alternative is to createanonymous arrays, each consisting of a desired slice of an <tt class="literal">@AoA</tt>subarray, and then put references to these anonymous arrays into<tt class="literal">@newAoA</tt>. We would then be writing references into <tt class="literal">@newAoA</tt>(subscripted once, so to speak) instead of subarray values into atwice-subscripted <tt class="literal">@newAoA</tt>. This method eliminates the innermostloop:<blockquote><pre class="programlisting">for ($x = 4; $x <= 8; $x++) { push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ];}</pre></blockquote><a name="INDEX-2101"></a>Of course, if you do this often, you should probably write asubroutine called something like <tt class="literal">extract_rectangle</tt>.And if you do it very often with large collections of multidimensionaldata, you should probably use the <tt class="literal">PDL</tt> (Perl DataLanguage) module, available from CPAN.</p><h3 class="sect2">9.1.5. Common Mistakes</h3><a name="INDEX-2102"></a><a name="INDEX-2103"></a><a name="INDEX-2104"></a><p>As mentioned earlier, Perl arrays and hashes are one-dimensional. InPerl, even "multidimensional" arrays are actually one-dimensional, butthe values along that dimension are references to other arrays, whichcollapse many elements into one. If you print these values outwithout dereferencing them, you will get thestringified references rather than the data you want. For example,these two lines:<blockquote><pre class="programlisting">@AoA = ( [2, 3], [4, 5, 7], [0] );print "@AoA";</pre></blockquote>result in something like:<blockquote><pre class="programlisting">ARRAY(0x83c38) ARRAY(0x8b194) ARRAY(0x8b1d0)</pre></blockquote>On the other hand, this line displays <tt class="literal">7</tt>:<blockquote><pre class="programlisting">print $AoA[1][2];</pre></blockquote></p><p>When constructing an array of arrays, remember to compose newreferences for the subarrays. Otherwise, you will just createan array containing the element counts of the subarrays, likethis:<blockquote><pre class="programlisting">for $i (1..10) { @array = somefunc($i); $AoA[$i] = @array; # WRONG!}</pre></blockquote>Here <tt class="literal">@array</tt> is being accessed in a scalar context,and therefore yields the count of its elements, which is dutifullyassigned to <tt class="literal">$AoA[$i]</tt>. The proper way to assign thereference will be shown in a moment.</p><p>After making the previous mistake, people realize they need to assign areference, so the next mistake people naturally make involves taking areference to the same memory location over and over again:<blockquote><pre class="programlisting">for $i (1..10) { @array = somefunc($i); $AoA[$i] = \@array; # WRONG AGAIN!}</pre></blockquote>Every reference generated by the second line of the <tt class="literal">for</tt> loop is thesame, namely, a reference to the single array <tt class="literal">@array</tt>. Yes, thisarray changes on each pass through the loop, but when everything issaid and done, <tt class="literal">$AoA</tt> contains 10 references to the same array,which now holds the last set of values assigned to it. <tt class="literal">print@{$AoA[1]}</tt> will reveal the same values as <tt class="literal">print @{$AoA[2]}</tt>.</p><p>Here's a more successful approach:<blockquote><pre class="programlisting">for $i (1..10) { @array = somefunc($i); $AoA[$i] = [ @array ]; # RIGHT!}</pre></blockquote>The brackets around <tt class="literal">@array</tt> create a new anonymousarray, into which the elements of <tt class="literal">@array</tt> arecopied. We then store a reference to that new array.</p><p>A similar result--though more difficult to read--would be produced by:<blockquote><pre class="programlisting">for $i (1..10) { @array = somefunc($i); @{$AoA[$i]} = @array;}</pre></blockquote>Since <tt class="literal">$AoA[$i]</tt> needs to be a new reference, the reference springs intoexistence. Then, the preceding <tt class="literal">@</tt> dereferences this new reference,with the result that the values of <tt class="literal">@array</tt> are assigned (in listcontext) to the array referenced by <tt class="literal">$AoA[$i]</tt>. You might wishto avoid this construct for clarity's sake.</p><p>But there <em class="emphasis">is</em> a situation in which you might use it. Suppose <tt class="literal">@AoA</tt>is already an array of references to arrays. That is, you've madeassignments like:<blockquote><pre class="programlisting">$AoA[3] = \@original_array;</pre></blockquote>And now suppose that you want to change <tt class="literal">@original_array</tt> (that is, youwant to change the fourth row of <tt class="literal">$AoA</tt>) so that it refers to theelements of <tt class="literal">@array</tt>. This code will work:<blockquote><pre class="programlisting">@{$AoA[3]} = @array;</pre></blockquote>In this case, the reference itself does not change, but the elementsof the referenced array do. This overwrites the values of<tt class="literal">@original_array</tt>.</p><p>Finally, the following dangerous-looking code actually works fine:<blockquote><pre class="programlisting">for $i (1..10) { my @array = somefunc($i); $AoA[$i] = \@array;}</pre></blockquote>That's because the lexically scoped <tt class="literal">my @array</tt> variable is createdafresh on each pass through the loop. So even though it looks asthough you've stored the same variable reference each time, you haven't.This is a subtle distinction, but the technique can produce moreefficient code, at the risk of misleading less-enlightened programmers.(It's more efficient because there's no copy in the final assignment.)On the other hand, if you have to copy the values anyway (which thefirst assignment in the loop is doing), then you might as well use thecopy implied by the brackets and avoid the temporary variable:<blockquote><pre class="programlisting">for $i (1..10) { $AoA[$i] = [ somefunc($i) ];}</pre></blockquote>In summary:<blockquote><pre class="programlisting">$AoA[$i] = [ @array ]; # Safest, sometimes fastest$AoA[$i] = \@array; # Fast but risky, depends on my-ness of array@{ $AoA[$i] } = @array; # A bit tricky</pre></blockquote>Once you've mastered arrays of arrays, you'll want to tackle morecomplex data structures. If you're looking for C structures or Pascalrecords, you won't find any special reserved words in Perl to setthese up for you. What you get instead is a more flexible system. Ifyour idea of a record structure is less flexible than this, or ifyou'd like to provide your users with something more opaque and rigid,then you can use the object-oriented features detailed in <a href="ch12_01.htm">Chapter 12, "Objects"</a>.</p><p><a name="INDEX-2105"></a><a name="INDEX-2106"></a><a name="INDEX-2107"></a><a name="INDEX-2108"></a><a name="INDEX-2109"></a>Perl has just two ways of organizing data: as ordered lists stored inarrays and accessed by position, or as unordered key/value pairsstored in hashes and accessed by name. The best way to represent arecord in Perl is with a hash reference, but how you choose toorganize such records will vary. You might want to keep an orderedlist of these records that you can look up by number, in which caseyou'd use an array of hash references to store the records. Or, youmight wish to look the records up by name, in which case you'dmaintain a hash of hash references. You could even do both at once,with pseudohashes.</p><p>In the following sections, you will find code examples detailing howto compose (from scratch), generate (from other sources), access, anddisplay several different data structures. We first demonstrate threestraightforward combinations of arrays and hashes, followed by a hashof functions and more irregular data structures. We end with ademonstration of how these data structures can be saved. Theseexamples assume that you have already familiarized yourself with theexplanations set forth earlier in this chapter.<a name="INDEX-2110"></a><a name="INDEX-2111"></a><a name="INDEX-2112"></a></p><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch08_05.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch09_02.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">8.5. Braces, Brackets, and Quoting</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">9.2. Hashes of Arrays</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -