You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
488 lines
30 KiB
488 lines
30 KiB
<!doctype html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|
<title>My Learning Website</title> |
|
<link href="/styles/styles.css" rel="stylesheet" type="text/css"> |
|
<link href="/programming/styles/styles.css" rel="stylesheet" type="text/css"> |
|
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> |
|
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> |
|
<!--[if lt IE 9]> |
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> |
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> |
|
<![endif]--> |
|
</head> |
|
<body> |
|
|
|
<div class="banner"> |
|
<h1 class="courselink">Perl 5 Essential Traning</h1> |
|
<h2 class="lecturer">LinkedIn Learning : Bill Weinman</h2> |
|
<h2 class="episodetitle">References and Structures</h2> |
|
</div> |
|
|
|
<article> |
|
<h2 class="sectiontitle">Understanding Values and References</h2> |
|
<p>A reference is a scalar value that refers to some value. This value might be another scalar value, but it can also be an array, a hash or a function. The general syntax for creating a variable is</p> |
|
<p class="inset">reference = \value</p> |
|
<p>This is shown in figure 103.</p> |
|
<img src="images/image2.png" alt="Different types of references"> |
|
<p class =>Figure 103 - the different types of references</p> |
|
<p>You can access the value by dereferencing using the appropriate symbol and enclosing the reference in curly braces.</p> |
|
<img src="images/image3.png" alt="Dereferencing a reference"> |
|
<p class="caption">Figure 104 - dereferencing a reference (accessing a value pointed to by the reference)</p> |
|
<p>We can also use the arrow operator to dereference and access structure members at the same time.</p> |
|
<img src="images/image4.png" alt="Accessing and dereferencing members"> |
|
<p class="caption">Figure 105 - accessing and dereferencing members using the arrow operator</p> |
|
<p>References allow us to use anonymous arrays, hashes and functions which can be handy for creating complex data structures.</p> |
|
<p>The ref can be used to check the type if a reference and this is demonstrated by the code in figure 106.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # ref.pl by Philip Osztromok |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $scalar; |
|
8. my @array; |
|
9. my %hash; |
|
10. |
|
11. my $rs = \$scalar; |
|
12. my $ra = \@array; |
|
13. my $rh = \%hash; |
|
14. |
|
15. print "$rs is a reference to a "; say ref $rs; |
|
16. print "$ra is a reference to a "; say ref $ra; |
|
17. print "$rh is a reference to a "; say ref $rh;</pre> |
|
<p class="caption">Figure 106 - using ref to determine the type of a reference</p> |
|
<p>The output from this is shown in figure 107.</p> |
|
<pre class="inset"> |
|
SCALAR(0x48e150) is a reference to a SCALAR |
|
ARRAY(0x48e0c0) is a reference to a ARRAY |
|
HASH(0x496ed8) is a reference to a HASH</pre> |
|
<p class="caption">Figure 107 - the output we get using the ref function</p> |
|
<p>References are commonly seen in Perl code and are used to create rich and complex data structures.</p> |
|
<p>There is an interesting tutorial on references on the website of <a href=" http://wwwacs.gantep.edu.tr/docs/perl-ebook/ch18.htm">Gazaiantep University</a> in Turkey and this is actually part of an online version of the book, <a href=" http://wwwacs.gantep.edu.tr/docs/perl-ebook/ch18.htm">Teach Yourself Perl 5 in 21 Days</a>.</p> |
|
<h2 class="sectiontitle">Array References</h2> |
|
<p>Array references are demonstrated with the code in figure 108.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # arrayref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my @array = qw( one two three four five ); |
|
8. my $ref = \@array; |
|
9. say foreach @{$ref};</pre> |
|
<p class="caption">Figure 108 - array.pl demonstrating the use of an array reference</p> |
|
<p>We have created an array on line 7 and a reference to that array on line 8. On line 9, we are using a foreach loop to output the contents of the array but note that we are accessing the array by its reference rather than by its name.</p> |
|
<p>The curly braces enclosing the reference on line 9 may be omitted in some circumstances and still work, but it isn’t always easy to tell in advance whether they are required so best practice is probably just to always include them.</p> |
|
<p>We can also initialize the reference directly with an anonymous array as shown in figure 109.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # arrayref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = [ qw( one two three four five ) ]; |
|
8. say foreach @{$ref};</pre> |
|
<p class="caption">Figure 109 - initializing a reference to an array with an anonymous array</p> |
|
<p>This allows us to remove line 7 from the code in figure 108. So this works in exactly the same way as the version that we saw in figure 108, but uses an anonymous array. It is often the case that if you don’t otherwise need to refer to the array, this is a more convenient way of creating and manipulating an array reference.</p> |
|
<p>Note that the anonymous array is enclosed in square brackets. If we used parentheses instead, like this</p> |
|
<p class="inset">my $ref = ( qw( one two three four five ) );</p> |
|
<p>or if we just omit them like this</p> |
|
<p class="inset">my $ref = qw( one two three four five );</p> |
|
<p>we will probably see this as the output</p> |
|
<pre class="inset"> |
|
philip@raspberrypi:~/learning/perl/perl5EssentialTraining/Ch11 $ perl arrayref.pl |
|
Useless use of a constant ("one") in void context at arrayref.pl line 7. |
|
Useless use of a constant ("two") in void context at arrayref.pl line 7. |
|
Useless use of a constant ("three") in void context at arrayref.pl line 7. |
|
Useless use of a constant ("four") in void context at arrayref.pl line 7. |
|
Can't use string ("five") as an ARRAY ref while "strict refs" in use at arrayref.pl line 8.</pre> |
|
<p class =>Figure 110 - the output we see when trying to create a reference to an anonymous array without using square brackets</p> |
|
<p>This may look a little strange in the context of Perl because we have largely, to this point, treated lists and arrays as being more or less synonymous so it often doesn’t matter if we use square brackets and in most cases, we don’t (or haven’t, at least). In other languages, you will always see square brackets for an array.</p> |
|
<p>In any case, the error here is caused by the fact that we are using list syntax and we cannot create a reference to a list so the distinction between array and list is important here. That being said, there may be some circumstances where Perl will allow this type of syntax so you may get away with omitting the square brackets, but it is probably better to include them. It’s also a good idea to be familiar with the error in case you do see it because it could be quite tricky to try to debug this if you haven’t seen the error before.</p> |
|
<p>Just as a side note, the reason why you can’t create a reference to a list is because a list is a literal object so it doesn’t have an address in memory that you can create a reference to.</p> |
|
<p>Recall that an array is essentially an array of scalar values so we can access each individual element of an array via its reference. For example, if we want to set the value of the array we saw in figure 108 or 109 to, let’s sat first, we can do that with the command</p> |
|
<p class="inset">${ref}[0] = ‘first’;</p> |
|
<p>In figure 111, we have the full version of our code where we are setting a reference to an anonymous array and then changing the value of the first element to ‘first’ and outputting the modified array.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # arrayref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = [ qw( one two three four five ) ]; |
|
8. ${$ref}[0] = 'first'; |
|
9. say foreach @{$ref};</pre> |
|
<p class =>Figure 111 - the same code that we saw in figure 109, except we are accessing an individual element of the array before outputting it</p> |
|
<p>So, in line 9 we have dereferenced and accessed the array. There is an easier way to do this, as we saw earlier, and that is to use the arrow operator.</p> |
|
<p class="inset">$ref->[0] = 'first';</p> |
|
<p>This is a slightly easier way to do the same thing and you will see this syntax used more commonly.</p> |
|
<h2 class="sectiontitle">Hash References</h2> |
|
<p>Hash references in Perl are convenient and easy to use so you may see more often than straightforward named hashes. This is demonstrated by the code shown in figure 112.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # hashref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my %hash = ( |
|
8. one => 'uno', |
|
9. two => 'dos', |
|
10. three => 'tres', |
|
11. four => 'quatro', |
|
12. five => 'cinco' |
|
13. ); |
|
14. |
|
15. my $ref = \%hash; |
|
16. foreach my $k ( sort keys %{$ref}) { |
|
17. say "$k : ${$ref}{$k}"; |
|
18. }</pre> |
|
<p class="caption">Figure 112 - hashref.pl demonstrating the use of a hash reference</p> |
|
<p>Here, we are creating a hash on lines 7 to 13. On line 15, we are creating a reference to the array. In the foreach loop on line 16, we are dereferencing the hash and sorting it with sort keys and inside the loop (on line 17), each individual element is dereferenced using a key. The output from this is (note that the keys are sorted alphabetically)</p> |
|
<pre class="inset"> |
|
five : cinco |
|
four : quatro |
|
one : uno |
|
three : tres |
|
two : dos </pre> |
|
<p>As with arrays, we can initialise our hash reference with an anonymous hash so this gives us the code shown in figure 113.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # hashref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = { |
|
8. one => 'uno', |
|
9. two => 'dos', |
|
10. three => 'tres', |
|
11. four => 'quatro', |
|
12. five => 'cinco' |
|
13. }; |
|
14. |
|
15. foreach my $k ( sort keys %{$ref}) { |
|
16. say "$k : ${$ref}{$k}"; |
|
17. }</pre> |
|
<p class="caption">Figure 113 - initialising a hash reference with an anonymous hash</p> |
|
<p>Note that just as we created a reference to an anonymous array by placing the array in square brackets, when we create a reference to an anonymous hash, we place the hash in curly braces and it is important to remember this.</p> |
|
<p>When we run this, we see that the output is exactly the same.</p> |
|
<p>If we want to dereference an element of the hash, we can do that. Let’s say, for instance, that we want to change the value referenced by the key one to the number 1. We can do that by adding the line</p> |
|
<p class="inset">${$ref}{one} = 1;</p> |
|
<p>When we run the code again, we will see the output is now</p> |
|
<pre class="inset"> |
|
five : cinco |
|
four : quatro |
|
one : 1 |
|
three : tres |
|
two : dos</pre> |
|
<p>Again, we can use the arrow operator instead which is both simpler and more common</p> |
|
<p class="inset">$ref->{one} = 1;</p> |
|
<p>This gives us the same result. The arrow operator can also be used inside the interpolated string. This version of the code is shown in figure 114.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # hashref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = { |
|
8. one => 'uno', |
|
9. two => 'dos', |
|
10. three => 'tres', |
|
11. four => 'quatro', |
|
12. five => 'cinco' |
|
13. }; |
|
14. |
|
15. $ref->{one} = 1; |
|
16. |
|
17. foreach my $k ( sort keys %{$ref}) { |
|
18. say "$k : $ref->{$k}"; |
|
19. }</pre> |
|
<p class="caption">Figure 114 - the final version of hashref.pl which uses the arrow operator to dereference and access elements of the hash</p> |
|
<h2 class="sectiontitle">Function References</h2> |
|
<p>Function references are not as common as array or hash references in Perl but they can be quite useful. The code in figure 115 demonstrates the use of a function reference.<p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # funcref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = \&func; |
|
8. &{$ref}(); |
|
9. |
|
10. sub func { |
|
11. say "This is the excellent function."; |
|
12. } </pre> |
|
<p class="caption">Figure 115 - funcref.pl which demonstrates the use of a function reference</p> |
|
<p>The function is defined on lines 10 to 12 and the function reference is created on line 7. On line 8, the function is dereferenced and called. Note the parentheses after the function reference on line 8, this is the function call operator.</p> |
|
<p>We can also use the arrow operator so we could call the function with</p> |
|
<p class="inset">$ref->();</p> |
|
<p>Again, this is the more common form of the syntax where we are avoiding the need to dereference.</p> |
|
<p>We can also reference an anonymous function and this is shown in figure 116.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # funcref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = sub { say "Hi! I'm an anonymous function!" }; |
|
8. &{$ref}(); |
|
9. |
|
10. sub func { |
|
11. say "This is the excellent function."; |
|
12. } </pre> |
|
<p class="caption">Figure 116 - funcref2.pl which creates a reference to an anonymous function</p> |
|
<p>Note that we still have the declared function between lines 10 and 12. We also created the function reference on line 7 and called the function on line 8.</p> |
|
<p>However, rather than referencing the declared function, we have created an anonymous function with the keyword sub followed by the body of the function in curly braces and the reference points to this function. So when we call the function on line 8, we are calling the anonymous function so the output when we run this is</p> |
|
<p class="inset">Hi! I'm an anonymous function!</p> |
|
<p>There is no call to the declared function, so we no longer see its output.</p> |
|
<p>One interesting aspect of function references is that they allow us to pass functions around as though they were scalar values. For example, we can have a function that returns a function. This is shown in figure 117.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # funcref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = func(); |
|
8. $ref->(); |
|
9. |
|
10. sub func { |
|
11. return sub {say "This function is returned from another function." }; |
|
12. }</pre> |
|
<p class="caption">Figure 117 - funcref3.pl demonstrating a function that returns a function</p> |
|
<p>This is very similar to the previous examples, the declared function is on lines 10 to 12 and this function returns a function. Again, the function is an anonymous function defined with the keyword sub and the body of the function in curly braces.</p> |
|
<p>We have created the function reference on line 8 and this time, it is initialised by calling the declared function and so it takes the returned value so it now references this anonymous function.</p> <p>On line 9, we use the reference with the arrow operator to call the anonymous function.</p> |
|
<p>Another interesting aspect of this is demonstrated with the code in figure 118.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # funcref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref = func(); |
|
8. $ref->(); |
|
9. |
|
10. sub func { |
|
11. my $s = "This string is local to func."; |
|
12. return sub {say $s }; |
|
13. }</pre> |
|
<p class="caption">Figure 118 - funcref3.pl - a function that returns a function that violates the scope of a variable in Perl</p> |
|
<p>This is similar to the code shown in figure 117, but rather than use a string literal in the returned function, we have declared a string in func and the function that is returned outputs this string.</p> |
|
<p>Lines 7 and 8 are unchanged. The interesting point here is that this shouldn’t work. We are calling the anonymous function on line 8 using the function reference. The function outputs the value of the string $s which was created inside the function on line 10 to 13 so it should not be accessible outside the function.</p> |
|
<p>When we run this, we see that it does work and we get the output</p> |
|
<p class="inset">This string is local to func.</p> |
|
<p>Of course, the reason this works is the interesting point and this is because of something called a closure. The anonymous function (created inside the declared function) encloses any variable that it needs so that it can access them when it is called. This is a simple enough concept, but it is also very powerful.</p> |
|
<p>Another example of this, which helps to demonstrate what a powerful feature this can be, is shown in figure 119.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # closure.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $fref1 = make_counter(1); |
|
8. my $fref42 = make_counter(42); |
|
9. |
|
10. say $fref1->(); |
|
11. say $fref1->(); |
|
12. say $fref1->(); |
|
13. |
|
14. say $fref42->(); |
|
15. say $fref42->(); |
|
16. say $fref42->(); |
|
17. |
|
18. say $fref1->(); |
|
19. say $fref42->(); |
|
20. say $fref1->(); |
|
21. say $fref42->(); |
|
22. |
|
23. sub make_counter { |
|
24. my $n = shift || 1; |
|
25. return sub { return $n++ }; |
|
26. }</pre> |
|
<p class="caption">Figure 119 - closure.pl which demonstrates the power of enclosing a variable in a returned function</p> |
|
<p>It is probably worth mulling over the code for a few moments to try to work out what the output will be. We have a function called make_counter and we have created two separate references to this function on lines 7 and 8 with the names fref1 and fref42. Now, the make_counter function expects an integer as an argument and assigns this value to the variable $n. The anonymous function uses the variable $n and increments its value which it then returns.</p> |
|
<p>So, we have two return values, the make_counter function returns an anonymous function and the anonymous function returns the value of $n plus 1.</p> |
|
<p>We create the function references by calling the make_counter function with a value of 1 for fref1 and 42 for fref42 so when we call the function referenced by fref1, we would exepct it to return 1 + 1 and similarly that fref42 would return 42 + 1.</p> |
|
<p>On lines 10 to 12, we call the function referenced by fref1 three times and then on lines 14 to16, we call the function referenced by 42 three times. On lines 18 to 21, we call both functions a further two times alternating between the two. The output we get from this is</p> |
|
<pre class="inset"> |
|
1 |
|
2 |
|
3 |
|
42 |
|
43 |
|
44 |
|
4 |
|
45 |
|
5 |
|
46</pre> |
|
<p>This surprised me a little because I expected that the first time these functions were called they would return 2 and 43, whereas they actually returned 1 and 42. This is because inside the anonymous function, we have used the post-increment operator so it is actually returning the value of n before it is incremented.</p> |
|
<p>In fact, this might be considered a bit weird since it means that the function is actually doing something after the function has returned a value (incrementing the counter) although it is technically incrementing the value as part of the execution of the return statement.</p> |
|
<p>You will notice that the value is incremented each time the functions are called. This is because the value originally passed to the make_counter to initialize the function references was not passed back, only the function containing its own copy of $n is returned.</p> |
|
<p>This has two important implications. The first is that the anonymous function is incrementing its own copy of $n every time it is called so the value is not reset to either 1 or 42. The second, which we can see in the output, is that each of the two references to the anonymous function is pointing to its own copy of the function so, although it is essentially the same function in both cases, each function is able to maintain its own copy of the counter.</p> |
|
<p>We can see this in the fact that fref1 outputs values from 1 up to 5 and fref42 outputs values from 42 up to 46. So, anonymous functions can be very powerful and their capabilities are enhanced when combined with closures.</p> |
|
<h2 class="sectiontitle">Finding the Type of a Reference</h2> |
|
<p>In figure 106, we saw some examples of using the ref function to determine the type of a reference. For example, the line</p> |
|
<p class="inset">print "$rs is a reference to a "; say ref $rs;</p> |
|
<p>output the type of value that the reference $rs refers to. As it happens, $rs is a reference to a scalar value so the output corresponding to this line is</p> |
|
<p class="inset">SCALAR(0x48e150) is a reference to a SCALAR</p> |
|
<p>That was a very simple example, but we can do a bit more with these returned values such as perform different processing for different types of reference and we have an example of that in figure 120.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # ref.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $ref1 = [ 1, 2, 3, 4 ]; |
|
8. my $ref2 = { one => 1, two => 2, three => 3, four => 4 }; |
|
9. my $ref3 = sub { say 'Hello' }; |
|
10. |
|
11. display_ref($ref1); |
|
12. display_ref($ref2); |
|
13. display_ref($ref3); |
|
14. |
|
15. sub display_ref { |
|
16. my $r = shift || ''; |
|
17. if ( ref($r) eq 'ARRAY' ) { |
|
18. say "This is an array"; |
|
19. } elsif ( ref ($r) eq 'HASH' ) { |
|
20. say "This is a hash"; |
|
21. }else { |
|
22. say "This is neither an array nor a hash"; |
|
23. } |
|
24. }</pre> |
|
<p class="caption">Figure 120 - ref2.pl which demonstrates how we can process a reference differently based on its type</p> |
|
<p>So, in ref2.pl, we create three references to different types of value, ref1 points to an array, ref2 points to a hash and ref3 points to a function. Each of these references is passed as an argument to the display_ref function declared on lines 15 to 24.</p> |
|
<p>The display_ref function uses shift to get the reference that has been passed to it on line 16.</p> |
|
<p>On line 17, we have a conditional statement which passes the reference to the ref function which returns the type of the value referred to and processes the code in the accompanying block (line 18) if the reference is to an array.</p> |
|
<p>Similarly, we have another conditional statement in the elsif clause (line 19) and accompanying code (line 20) which will be executed if the reference is to a hash.</p> |
|
<p>The code on line 22 is executed if neither of these conditions is met.</p> |
|
<p>The code being executed does nothing more than output the type of the reference, but we could add more complex processing if we needed to process the array or the hash or whatever is being referenced. In essence, this means that we can, for example, create a function that will iterate through some collection without knowing in advance whether the collection is an array or a hash so that can be very useful. For example, if we just want to output the contents of the array or hash, we could rewrite the display_ref function like this.</p> |
|
<pre class="inset"> |
|
1. sub display_ref { |
|
2. my $r = shift || ''; |
|
3. if ( ref($r) eq 'ARRAY' ) { |
|
4. say foreach @{$r}; |
|
5. } elsif ( ref ($r) eq 'HASH' ) { |
|
6. foreach my $k ( sort keys %{$r} ) { |
|
7. say "$k : $r->{$k}"; |
|
8. } |
|
9. }else { |
|
10. say "This is neither an array nor a hash"; |
|
11. } |
|
12. }</pre> |
|
<p class="caption">Figure 121 - the display_ref function displaying the contents of either an array or a hash</p> |
|
<p>When we run this, we will see that the function will output the contents of the array and the hash, depending on what type of reference it is called with.</p> |
|
<h2 class="sectiontitle">Mixed Data Structures</h2> |
|
<p>References allow us to mix different types of data into a single data structure and this can make them very powerful. Consider the code shown in figure 122.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # mixed.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $musicians = [ |
|
8. { name => 'Jimi Hendrix', instrument => 'Guitar', genre => 'Rock' }, |
|
9. { name => 'Miles Davis', instrument => 'Trumpet', genre => 'Jazz' }, |
|
10. { name => 'Yo-Yo Ma', instrument => 'Cello', genre => 'Classical' }, |
|
11. { name => 'Frank Zappa', instrument => 'Guitar', genre => 'Fusion' } |
|
12. ]; |
|
13. |
|
14. foreach my $m ( @{$musicians} ) { |
|
15. say "$m->{name}: $m->{instrument}, $m->{genre}"; |
|
16. }</pre> |
|
<p class="caption">Figure 122 - mixed.pl</p> |
|
<p>First of all, we have a reference on line 7. The square brackets on lines 7 and 12 enclose an anonymous array. Each element of the array (lines 8 to 11) is an anonymous hash enclosed in curly braces.</p> |
|
<p>In the foreach loop, each element of the array is dereferenced and output. The output is</p> |
|
<pre class="inset"> |
|
Jimi Hendrix: Guitar, Rock |
|
Miles Davis: Trumpet, Jazz |
|
Yo-Yo Ma: Cello, Classical |
|
Frank Zappa: Guitar, Fusion</pre> |
|
<p>We can also use push to add another element to the array like this.</p> |
|
<p class="inset">push @{$musicians}, {name => 'Elton John', instrument => 'Piano', genre =>'Rock'};</p> |
|
<p>If we run this now, we will see the additional line of output.</p> |
|
<p>In fact. we could have added all of the elements to the array via push statements as shown in the code in figure 123.</p> |
|
<pre class="inset"> |
|
1. #!/usr/bin/perl |
|
2. # mixed.pl by Bill Weinman <http://bw.org/contact/> |
|
3. |
|
4. use 5.28.0; |
|
5. use warnings; |
|
6. |
|
7. my $musicians = []; |
|
8. |
|
9. push @{$musicians}, { name => 'Jimi Hendrix', instrument => 'Guitar', genre => 'Rock' }; |
|
10. push @{$musicians}, { name => 'Miles Davis', instrument => 'Trumpet', genre => 'Jazz' }; |
|
11. push @{$musicians}, { name => 'Yo-Yo Ma', instrument => 'Cello', genre => 'Classical' }; |
|
12. push @{$musicians}, { name => 'Frank Zappa', instrument => 'Guitar', genre => 'Fusion' }; |
|
13. push @{$musicians}, {name => 'Elton John', instrument => 'Piano', genre =>'Rock'}; |
|
14. |
|
15. foreach my $m ( @{$musicians} ) { |
|
16. say "$m->{name}: $m->{instrument}, $m->{genre}"; |
|
17. }</pre> |
|
<p class="caption">Figure 123 - mixed2.pl where we start with an anonymous array and populate it with severl push statements</p> |
|
<p>This approach, where we start with an empty array and then push elements on to it is quite a common practice in Perl.</p> |
|
<p>Now, in this example, the fields all have one date item. That is, for each element we have one name, one instrument and one genre. However, we can adapt this to accommodate two or more entries. For example, let’s say we want to add ‘Vocal’ as an instrument for Elton John so the push statement for that element becomes</p> |
|
<p class="inset">push @{$musicians}, {name => 'Elton John', instrument => [ 'Piano', ‘Vocal’ ] genre =>'Rock'};</p> |
|
<p>So this is a straightforward case of replacing the string with an array containing two strings. If we make this change and go ahead and run the code, the output we get is</p> |
|
<pre class="inset"> |
|
Jimi Hendrix: Guitar, Rock |
|
Miles Davis: Trumpet, Jazz |
|
Yo-Yo Ma: Cello, Classical |
|
Frank Zappa: Guitar, Fusion |
|
Elton John: ARRAY(0x14d2ca8), Rock</pre> |
|
<p>So this has worked, but the array has been identified as an array. We have not extracted the individual elements of that array.</p> |
|
<p>We can do that within the foreach loop by checking if the instrument key maps to an array and expanding it if that is the case.</p> |
|
<p>We will use a ternary operator for that purpose as follows:</p> |
|
<p class="inset">my $inst = ref($m->{instrument}) eq 'ARRAY' ? join ('/', @{$m->{instrument}}) : $m->{instrument};</p> |
|
<p>So, we are checking the reference type for $m and we are returning a string value. If the conditiom evaluates to true (if $m is an array), the string returned is an array which has been converted to a single string with join. We haven’t covered join yet, but we will look at that later, but it seems to create a string with all of the elements in the array separated by /, presumably we could use other characters such as a comma as the separator.</p> |
|
<p>If the condition is false, the string returned is simply the original value. To finish off, we need to adapt the say statement so that it output this string rather than the string it gets back from the element in the original array, so this becomes</p> |
|
<p class="inset">say "$m->{name}: $inst, $m->{genre}";</p> |
|
<p>Now, if we run the code, our output is</p> |
|
<pre class="inset"> |
|
Jimi Hendrix: Guitar, Rock |
|
Miles Davis: Trumpet, Jazz |
|
Yo-Yo Ma: Cello, Classical |
|
Frank Zappa: Guitar, Fusion |
|
Elton John: Piano/Vocal, Rock</pre> |
|
<p>We can see that the entry for Elton John, we can see that the array has been expanded. </p> |
|
<p>To summarise this, we are returning a value that may be an array, we are assuming that otherwise it is a scalar and we are optionally processing this based on whether or not is an array using the ref operator.</p> |
|
<p>This demonstrates the fact that we can use references to create complex and elegant data structures and it is well worth taking the time to investigate the possibilities offered by this feature of Perl.</p> |
|
</article> |
|
|
|
<div class="btngroup"> |
|
<button class="button" onclick="window.location.href='functions.html';"> |
|
Previous Chapter - Functions |
|
</button> |
|
<button class="button" onclick="window.location.href='fileio.html';"> |
|
Next Chapter - File I/O |
|
</button> |
|
<button class="button" onclick="window.location.href='perl5essentialtraining.html'"> |
|
Course Contents |
|
</button> |
|
<button class="button" onclick="window.location.href='/programming/programming.html'"> |
|
Programming Page |
|
</button> |
|
<button class="button" onclick="window.location.href='/index.html'"> |
|
Home |
|
</button> |
|
</div> |
|
|
|
</body> |
|
</html> |