<h2class="lecturer">LinkedIn Learning : Bill Weinman</h2>
<h2class="episodetitle">Built_In Functions</h2>
</div>
<article>
<h2class="sectiontitle">The print() and say() functions</h2>
<p>Both of these functions are used for writing text to streams and this can, of course, include the console or screen.</p>
<p>The only difference between the two is that print() doesn’t add a line feed to the end of the line whereas say() does. For instance, if we look at the code in figure 136</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # hello.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. say "Hello, World!";
8. say "Hello, World!";
9. say "Hello, World!";</pre>
<pclass="caption">Figure 136 - hello.pl demonstrating the print() function</p>
<p>this is simply outputting the same string three times using the say() function so the output is</p>
<preclass="inset">
Hello, World!
Hello, World!
Hello, World!</pre>
<p>Now, if we use the print() function in place of the say() function and run the code again, the output is</p>
<p>We also see that the prompt (which appears after the code has finished executing) is also on the same line since there has been no line feed to take us to a new line.</p>
<p>It might be worth digressing slightly here and noting that the function names, print() and say() are followed by a pair of parentheses which emphasises the fact that they are outputting the argument passed to them. This could also be a list of arguments and each would be output in turn.</p>
<p>It’s quite normal to do this in other languages because it makes it clear that this is a function. I don’t normally bother here because unlike, for example, Java or Kotlin, the parentheses are not always required. In most cases, you will see print() and say() being used without them but I am including them here since functions is the topic of this chapter.</p>
<p>The print() function has always been available in Perl whereas the say() function was introduced with Perl 5.10. On an older system, you may well find that say() is not available.</p>
<p>We can use say() or print() to output a list. For example</p>
<pclass="inset">say (1, 2, 3);</p>
<p>will give the output</p>
<pclass="inset">123</p>
<p>Note there are no line feeds between the list elements because we are only outputting one list so there is only one say statement and therefore only one line feed at the end of the list. If we use the print() statement instead, the output looks almost the same except there is no line feed at the end so we won’t get the next prompt on its own line.</p>
<p>We could also create an array</p>
<pclass="inset">my @a = (1, 2, 3);</p>
<p>and output it with</p>
<pclass="inset">say @;</p>
<p>Similarly, we could create a hash</p>
<pclass="inset">my %h = ( one => 1, two =>2, three => 3 );</p>
<p>and output it with</p>
<pclass="inset">say %h;</p>
<p>We can also combine these and print both the array and the hash as a single list and the code for this is shown in figure 137.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # hello.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = (1, 2, 3);
8. my %h = ( one => 1, two => 2, three => 3 );
9.
10. say @a, %h;</pre>
<pclass="caption">Figure 137 - outputting an array and a hash with a single say statement</p>
<p>Again, we can output the contents of an array or a hash with a print statement with the only difference being the lack of a line feed at the end of the list. This gives us output like this</p>
<pclass="inset">123one1three3two2</p>
<p>This gives us a bit of a problem in that we often use say in order to provide line feeds so that we don’t get our output in one continuous stream. In this case, since the say statement is only giving us one line feed, we can use separators to format the output.</p>
<p>We do this in a similar way to the method used to enable binary mode. We set the separator with a command such as</p>
<pclass="inset">$, = ‘, ‘;</p>
<p>When we run the code now, the output will look like this.</p>
<p>You may recall that a hash is an unordered collection so the order in which they key value pairs are output is not constant.</p>
<p>We can, of course, use different separators. For example, if we change the specified separator to</p>
<pclass="inset">$, = ' -- ';</p>
<p>the output will be</p>
<pclass="inset">1 -- 2 -- 3 -- three -- 3 -- two -- 2 -- one -- 1</p>
<p>This method is useful, but has one major drawback in that it modifies all of the say() and print() functions for the rest of the script or until the separator is set to something else. In other words, it is a global change and for that reason, it is more common to use the join function to achieve the same effect.</p>
<p>The join function takes two arguments, the separator and a list. It them converts the list to a string with the elements separated. This is demonstrated with the code in figure 138.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # hello.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = (1, 2, 3);
8. my %h = ( one => 1, two => 2, three => 3 );
9.
10. my $a = join (', ', @a);
11. my $h = join (' -- ', %h);
12.
13. say $a;
14. say $h;</pre>
<pclass="caption">Figure 138 - hello.pl using join to format the output from an array and a hash</p>
<p>Here, we are creating an array called @a on line 7 and a hash called %h on line 8.</p>
<p>On line 10, we use the join function to create a string called $a with the contents of the array separated by a comma. Similarly, on line 11 we convert the hash to a string called $h using ‘ -- ‘ as the separator.</p>
<p>We then use two separate say() statements to output the two strings, giving us the output</p>
<preclass="inset">
1, 2, 3
three -- 3 -- one -- 1 -- two -- 2</pre>
<p>We can also use this more directly without first converting the lists to strings. Lines 10 to 14 could be replaced with a single line (although, since it uses a single say() statement, both lists would be output on the same line)</p>
<p>Notice that this also means we have to use the same separator for both lists since we are using join to create a single string.</p>
<p>It was mentioned earlier that both print() and say() send their output to a stream. By default, this is the STDOUT (standard output). Usually, the standard output is sent to the screen. Since this is the default option, we don’t need to explicitly specify it, but we can and we do that by putting the stream name after the say() or print() statement.</p>
<p>For instance, the previous statement we used with join to output an array and a hash as a single string could have been written as</p>
<pclass="inset">say STDOUT join ‘, ‘, @a, %h;</p>
<p>If we run the code again, we won’t see any change to the output because we are still outputting to the same stream, but we are being more explicit by naming the stream.</p>
<p>We could also output to the standard error stream (STDERR)</p>
<pclass="inset">say STDERR join ‘, ‘, @a, %h;</p>
<p>If we run the code again, we may see exactly the same output again because by default, errors are sent to the screen. They are separate streams, however, and they can be configured differently. For example, on the course video, errors are displayed in red so you will a difference in colour when sending output to STDERR. On my system, it is printed in white (as is the standard output) so I don’t see a change.</p>
</p>More information on the standard streams can be found in the <ahref="https://manpages.debian.org/jessie/manpages-dev/stderr.3.en.html">Debian Man Page</a> and there is an interesting discussion on changing the colour of the standard error stream on <ahref="https://serverfault.com/questions/59262/bash-print-stderr-in-red-color">StackExchange</a>.</p>
<h2class="sectiontitle">The die() Function</h2>
<p>The die() function does two things, it displays an error message and then terminates the execution of the script. The second point is important, because you would use the function when there is no point in allowing the script to continue running.</p>
<p>For example, if you consider the code we used to copy an image file, if the original file couldn’t be opened, we can’t copy it so the rest of the script wouldn’t achieve anything and may even do dome harm.</p>
<p>We will look at handling an ‘error’ condition without die() first. Consider the code shown in figure 139.</h1>
<preclass="inset">
1. #!/usr/bin/perl
2. # hello.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my $condition = 1;
8.
9. if ($condition) {
10. say 'Condition is true.';
11. } else {
12. say 'Condition is false.';
13. }
14.
15. say'After the condition.';</pre>
<pclass="caption">Figure 139 - hello2.pl demonstrating a simple error handling technique</p>
<p>We have a variable called $condition and we can think of this as representing some error condition so a false value would represent an error. In this case, we have given it a value of 1 so this is a true value.</p>
<p>We then simply output some text indicating whether the condition was true or false and then output another statement. If we run this code as shown, the output is</p>
<preclass="inset">
Condition is true.
After the condition.</pre>
<p>If we change the value of condition to 0 (a false value, representing an error condition) and run the code again, the output is</p>
<preclass="inset">
Condition is true.
After the condition.</pre>
<p>The point of the second line of output is that it shows that execution of the code when there is an error condition continues even after the code which runs when an error is encountered has been executed. In other words, encountering an error leads to some message being displayed but does not stop the script from continuing to execute.</p>
<p>If we change the code in the else clause to</p>
<pclass="inset">die 'Condition is false.';</p>
<p>so we are just replacing say with die, when we execute this with the value of $condition set to 0, the output we get is</p>
<pclass="inset">Condition is false at hello2.pl line 12.</p>
<p>So we have displayed the message in the else block and some additional information with the name of the file and the line number where the error has been encountered. Actually, I would guess that the line number is whatever line the die() function is called from.</p>
<p>Note that in this case, we do not see the second line of output because, in this case, die() has stopped execution of the script so nothing is executed if it comes after the call to die().</p>
<p>It might be worth noting a couple of differences between the output that I see and the output shown in the video. Firstly, I only get the filename whereas the error output in the video displays the filename and its absolute path. Secondly, the error is displayed in red in the video, but white in my terminal (which also displays standard output in white).</p>
<p>I assume that these are both configurable, hence the differences. It is worth noting that the different colour helps to emphasize the fact that the output we see is not being sent to the standard output, it is being sent to the standard error stream.</p>
<p>If we add a newline character to the end of the string on line 12, so</p>
<pclass="inset">die 'Condition is false.\n';</p>
<p>We don’t get the error message so the output is</p>
<pclass="inset">Condition is false.</p>
<p>This does still halt the execution of the script so we don’t see the second line of output.</p>
<p>If we want to have more control over what happens when an error is encountered, we can create our own function to display an error message and exit. The code for this is shown in figure 140.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # hello.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6. use subs qw( errorexit );
7. my $condition = 0;
8.
9. if ($condition) {
10. say 'Condition is true.';
11. } else {
12. errorexit 'Condition is false.';
13. }
14.
15. sub errorexit {
16. my $m = shift || 'Error message goes here!';
17. say STDERR "I've got a bad feeling about this: $m";
18. exit;
19. }</pre>
<pclass="caption">Figure 140 - hello3.pl demonstrating a custom function to print an error message and exit the script</p>
<p>The function itself is called errorexit and is defined on lines 15 to 19. On line 16, the string that is passed to this function is extracted with shift. Note that there is a default message so if errorexit is called without a string, $m takes the value of the default error message.</p>
<p>On line 17, we send a message to STDERR along with the error message in $m. On line 18, we exit the script with the exit command.</p>
<p>The function is called on line 12 and we have passed it the string, ‘Condition is false’ so this is the message we will display when the errorexit function is called.</p>
<p>We have also added a user subs directive with the name of our function, errorexit. This is not strictly required, but it allows us to call the function without parentheses. If we remove the directive, the code will still work if we change line 12 to</p>
<pclass="inset">errorexit ('Condition is false.');</p>
<p>As it stands, the errorexit doesn’t do much different from die and it doesn’t give us the stack trace that we get with die. However, it is possible to add the appropriate commands so that our function also provides this type of information. I guess that it is possible to output a line number but there is a good chance that it will give us a line number from the errorexit function, probably for the line where the exit statement is executed so its use may be limited. There is some information on this on the <ahref="https://www.perlmonks.org/?node_id=454413">perlmonks.org</a> website.</p>
<p>More importantly, we could add as much additional code to the errorexit function as is required so this might include a clean up, perhaps output some additional data for debugging means and so on.</p>
<h2class="sectiontitle">String Functions</h2>
<p>Perl has a number of useful functions for working with strings and we will start investigating them with a script that simply creates and then outputs a string. The code for this is shown in figure 141.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # string.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my $string = "This string has a bunch of useful characters in it.";
8. say $string;</pre>
<pclass="caption">Figure 141 - string.pl which simply creates and outputs a string</p>
<p>We saw this in use with the say function and you may recall that chomp removes a line ending, if there is one, from a string. We can actually pass the string to the chomp function like this</p>
<pclass="inset">say chomp string;</p>
<p>and this will return the number of characters removed by chomp. If we add this to string.pl, this will output 0 since there is no line ending to remove. However, if we add one to the string like this</p>
<pclass="inset">my $string = "This string has a bunch of useful characters in it.\n";</p>
<p>we will find that</p>
<pclass="inset">say chomp string;</p>
<p>now outputs a 1.</p>
<h2class="subsectiontitle">The chop Function</h2>
<p>Much as the say function is relatively new compared to print (and largely is a replacement for it), the chomp function is also a relatively recent introduction to Perl and largely replaces the chop function.</p>
<p>The chomp and chop functions essentially do the same thing and both are intended to remove an additional line ending from a line of text. However, unlike chomp, the chop function does this simply by assuming the last character is a new line character and deletes it. So, if we apply chop to the string in string.pl like this</p>
<pclass="inset">say chop $string;</p>
<p>the output is not a 1 or a zero as we saw with chomp, but the character that has been removed, in this case a full stop.</p>
<p>In most cases, we wouldn’t want to use chop (or chomp) like this, we are more interested in the string after the line feed has been removed rather than the line feed character. If we add a line of code such as<p>
<pclass="inset">chop $string</p>
<p>and then we output the string with</p>
<pclass="inset">say $string</p>
<p>bear in mind that the value of $string before chop is applied is</p>
<preclass="inset">
This string has a bunch of useful characters in it.
</pre>
<p>The output is therefore</p>
<pclass="inset">This string has a bunch of useful characters in it</p>
<p>So, in essence, chop and chomp do the same thing, but chomp does it a little more intelligently. If we had applied chomp to $string rather than chop, the string would not be changed because there is no line feed character to remove. The chop function assumes that there is and therefore simply removes the character with the result that when we output the string (as shown above) we can see that the full stop at the end of the string has been removed.<p>
<p>The substring function can be used for two purposes. To get a portion of a string or to alter a portion of the string.</p>
<p>Let’s say we want to get the characters ‘string’ from $string. To do this, we pass three arguments to the substring function. The string we want to extract a substring from, an integer representing the position of the first character to extract (the starting point of our substring) and an integer representing the number of characters to be returned.</p>
<p>The statement</p>
<pclass="inset">say substr $string, 5, 6;</p>
<p>will give us the output</p>
<pclass="inset">string</p>
<p>We can also omit the second argument (the 6), the function will return everything after the specified character so</p>
<pclass="inset">say substr $string, 5;</p>
<p>this will give us the output</p>
<pclass="inset">string has a bunch of useful characters in it.</p>
<p>We can also use the substring function to edit a string and we do that bu passing the function the same arguments as we did before (the string, the first character and yjr number of characters to find) and adding another argument, the string we want to replace it with.</p>
<p>So the substring function is now called with</p>
<p>The output we get from running this amended script is</p>
<pclass="inset">This foo a bunch of useful characters in it.</p>
<h2class="subsectiontitle">The index Function</h2>
<p>The index function is, in some ways, complementary to the substring function. With the substring function, we specified the location of a particular string. The index function takes a string and a substring as arguments and returns the position of the substring within the string. So</p>
<pclass="inset">say index $string, 'in';</p>
<p>If we count from the left, starting at 0, we find the substring (‘in’) at position 8 so that’s the output we get. The substring appears more than once but we get the position of the first occurrence.</p>
<p>We can skip past the first occurrence by providing the string function with an additional argument, say 10, which causes it to start searching from that position so</p>
<pclass="inset">say index $string, 'in', 10;</p>
<p>returns 45. That is, the position of the second occurrence of the substring.</p>
<p>Note that if the substring is not found in the string, the function returns -1.</p>
<p>By default, we are searching from the start of the string (the left), but we can also search from the end backwards by using the rindex (right index function).</p>
<pclass="inset">say rindex $string, 'in';</p>
<p>This will return 45. Note that we have two occurrences of the substring at positions 8 and 45 and since we are starting at the end, we find the second occurrence first. Again, we can add an additional parameter to skip some characters.</p>
<pclass="inset">say rindex $string, 'in', 10;</p>
<p>This returns 8, the second occurrence of the substring when counting from the end.</p>
<p>Of course, there is no great significance to the value 10 here. It just so happens that with both index and rindex, 10 is large enough for us up to skip past the first occurrence of the substring and find the second. I guess that in a real example, if you wanted to find a second occurrence you would first of all get the position of the first. You can then use that value plus one in order to skip past the first occurrence.</p>
<h2class="subsectiontitle">An Exercise with the index Function</h2>
<p>As an exercise, I created a function called count_index which takes two arguments, a string and a substring, and counts the number of occurrences of the substring within that string. The script including this function is shown in figure 143.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # count_index.pl by Philip Osztromok
3.
4. use 5.28.0;
5. use warnings;
6. use subs qw (count_index );
7.
8. my $string = "This string has a bunch of useful characters in it.";
9. say count_index ($string, 'a');
10.
11. sub count_index {
12. my $s = shift;
13. my $u = shift;
14. my $p=0;
15. my $c=0;
16. my $i=1;
17.
18. until ( $i < 0) {
19. $i = index($s, $u, $p);
20. if ($i > 0) { $c++ } ;
21. $p=$p+$i+1;
22. }
23. return $c;
24. }</pre>
<pclass="caption">Figure 143 - my count_index function</p>
<p>So, this is using shift to pull out the two arguments passed to the function and initialising variables. The theory is that it will run until no match is found and it starts by checking the index position of the substring using a position of 0 (that is, not skipping any characters so simply checking whether the substring is found. If it is found, the count variable ($c) is incremented and the loop goes on to the next iteration.</p>
<p>If it is not found, the loop terminates and the value of the count variable is returned.</p>
<p>The function doesn’t quite work correctly. It seems to return the correct value if the substring is more than one character or if the substring is not found. However, if the substring is a single character and there is more than one occurrence, the value returned is one less than it should be. For example, there is one l and if we search for a substring of ‘l’, the value returned is 1. On the other hand, there are five occurrences of the letter s and if we search for a substring of ‘s’, the value returned is 4.</p>
<p>I will further refine this and try to get it working later and if I do find a solution, I will probably add it as an appendix so see appendix _ for a correct solution!</p>
<p>The reverse function has two modes of operation. If applied to a scalar variable such as a string, it reverses it.</p>
<p>In a list context, if the list is ordered, reverse will reverse the order of the elements in the list.</p>
<p>or example, the statement</p>
<pclass="inset">say scalar reverse ($string);</p>
<p>will produce the output</p>
<pclass="inset">.ti ni sretcarahc lufesu fo hcnub a sah gnirts sihT</p>
<p>If we omit the keyword scalar, the reverse function operates in list context because that is the normal mode of operation for say, so the statement</p>
<pclass="inset">say scalar reverse ($string);</p>
<p>will produce the output</p>
<pclass="inset">This string has a bunch of useful characters in it.</p>
<p>Essentially, the string has been treated as a list of strings and since there is only one element, reverse has no effect.</p>
<p>The statement</p>
<pclass="inset">say reverse (1, 2, 3, 4 ,5)</p>
<p>will give us the output</p>
<pclass="inset">54321</p>
<p>So, here, the list is ordered so reverse is returning the same list in reverse order.</p>
<p>Similar to the lower case function, but returns a string entirely in upper case. So the statement</p>
<pclass="inset">say uc string;</p>
<p>will produce the output</p>
<pclass="inset">THIS STRING HAS A BUNCH OF USEFUL CHARACTERS IN IT.</p>
<h2class="subsectiontitle">The uppercase First Function</h2>
<p>This sets the first character of a string to upper case. If we had created a string like so</p>
<pclass="inset">my $other_string = 'this is a string. this is another string. this is the third string.'</p>
<p>the command</p>
<pclass="inset">say ucfirst $other_string;</p>
<p>will produce the output</p>
<pclass="inset">This is a string. this is another string. this is the third string.</p>
<p>You will notice that ucfirst doesn’t do any checking of the string, it simply sets the case of the first character in the string to upper case.</p>
<p>There are other string functions and these are listed at <ahref="https://perldoc.perl.org/functions">https://perldoc.perl.org/functions</a>.</p>
<h2class="subsectiontitle">Numeric Functions</h2>
<p>Perl has a full complement of math operators and functions. This, of course includes standard arithmetic operators.</p>
<p>Let’s start with a simple sample script, shown in figure 144, that simply creates two numeric variables, performs an arithmetic operation and then outputs the result.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # num.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my $a = 47;
8. my $b = 150;
9.
10. my $x = $a + $b;
11.
12. say "result is: $x";</pre>
<pclass="caption">Figure 144 - a simple script demonstrating an arithmetic operation</p>
<p>Not surprisingly, the output is</p>
<pclass="inset">result is: 197</p>
<h2class="subsectiontitle">The int Function</h2>
<p>The division operator doesn’t always work in the same way for different programming languages. If we change the operation on line 10 to</p>
<pclass="inset">my $x = $b / $a;</p>
<p>I would expect this to perform integer division and therefore return 3 and actual output is</p>
<pclass="inset">result is: 3.19148936170213</p>
<p>So that is something you may need to be careful with</p>
<p>As it happens, Perl doesn’t have an integer division operator. If we want to perform integer division, we need to use the int function.</p>
<pclass="inset">my $x = int $b / $a;</p>
<p>This gives us the output we expected</p>
<pclass="inset">result is: 3</p>
<p>Since we are taking the integer part here, there is no rounding up so the result would be 3 even if the actual value of the division was nearer to 4. However, we can add 0.5 to the result and the overall result would be rounded up. To make this clearer, let’s assume that $a has a value of 51 with $b still having a value of 150. If we do</p>
<pclass="inset">my $x = $b / $a;</p>
<p>we get the output</p>
<pclass="inset">result is: 2.94117647058824</p>
<p>So that is nearly 3. Now, if we use the int function</p>
<pclass="inset">my $x = int $b / $a;</p>
<p>we get the output</p>
<pclass="inset">result is: 2</p>
<p>So we can see, quite clearly, that the result has not been rounded. Now, if we amend this to</p>
<pclass="inset">my $x = int $b / $a + 0.5;</p>
<p>we get the output</p>
<pclass="inset">result is: 3</p>
<p>Of course, we are not really rounding it up, what we are doing is ensuring that the non-integer part is at least 0.5, so if it was already larger than 0.5 (which it was in this case) we are going to be moving up to the next integer value, in this case 3. If it is less than 0.5 to begin with, for example if the result of the division is 2.18, adding 0.5 to this will not take us up to the next integer value, so we see the value of the integer division which is 2.</p>
<h2class=>ThemoduloOperator</h2>
<p>In spite of this, the modulo operator does work as expected so</p>
<pclass="inset">my $x = int $b % $a;</p>
<p>gives us the output</p>
<pclass="inset">result is: 9</p>
<h2class="subsectiontitle">The abs Function</h2>
<p>This takes the abs value of a result. Let’s change the value of $a back to 47 and perform the operation</p>
<p>This is handy for calculating the value of pi although I’m not enough of a mathematician to know exactly what it does or what else it may be used for.</p>
<p>The statement</p>
<pclass="inset">my $x = atan2(1,1)*4;</p>
<p>gives the output</p>
<pclass="inset">result is: 3.14159265358979</p>
<h2class="subsectiontitle">The sqrt Function</h2>
<p>This quite simply returns the square root of the number passed to it so</p>
<pclass="inset">my $x = sqrt(64);</p>
<p>will give us the output</p>
<pclass="inset">result is: 8</p>
<p>There is no power function, but there is a power operator so</p>
<p>Perl provides a number of functions for converting the base of numbers, so we might, for instance, convert a hex number to decimal with</p>
<pclass="inset">my $x = hex 'FF';</p>
<p>The case is not important here, but this will give us the output</p>
<pclass="inset">result is: 255</p>
<p>Similarly, we can convert from octal with</p>
<pclass="inset">my $x = oct 377;</p>
<p>and this will also give the output</p>
<pclass="inset">result is: 255</p>
<p>You might also expect that a similar function, for example bin, would convert numbers from binary to decimal, but this is not the case. From the documentation, in fact, there doesn’t seem to be a function to do that.</p>
<h2class="subsectiontitle">The rand Function</h2>
<p>We can generate a random number between 0 and 1 with the rand function, which taps into the computer’s random number generator. For example, we could simply output a random number with</p>
<pclass="inset">say "Result is : ", rand;</p>
<p>and this will give us output that looks something like this</p>
<pclass="inset">Result is : 0.975946828098134</p>
<p>Of course, we would expect that this would produce a different number each time the code is executed and if we run the code several times, we do indeed see a different result each time.</p>
<p>We can seed the random number generator with the srand command</p>
<pclass="inset">srand ($a + $b);</p>
<p>for example. Now, every time we run the code we get the same output every time and we can see this in figure 145.</p>
<imgsrc="images/image8.png"alt="outputting a random number">
<pclass="caption">Figure 145 - the results we see when running our code to output a random number several times and also where the random number generator has been seeded</p>
<p>We can also omit the argument passed to srand</p>
<pclass="inset">srand ();</p>
<p>The result is that srand will actually use rand to obtain a value for the seed. Since we are essentially seeding the random number generator every time we run the code, the result is that we do not get the same number every time we run the code.</p>
<p>In fact, rand implicitly seeds itself with a call to srand() so seeding the random number generator in this way has no effect since it would happen automatically anyway.</p>
<p>If we want to get a random number between 0 and some integer value, let’s say 100, we can pass an argument to rand.</p>
<preclass="inset">
say "Result is : ", rand 100;
</pre>
<p>This will give us output that looks like</p>
<pclass="inset">Result is : 71.8882843227224</p>
<p>We can also use int with this in order to get an integer value. Let’s say we want to get a number between 1 and 5.</p>
<p>We might do that with</p>
<pclass="inset">say "Result is : ", int rand 5;</p>
<p>To demonstrate this, I have removed the seed and the results of running the code several times are shown in figure 146.</p>
<imgsrc="images/image9.png"alt="generating a random number">
<pclass="caption">Figure 146 - generating a number between 1 and 5 (or trying to)!</p>
<p>There is a problem here and that is the random number generates a number between 0 and 5. I’m not completely sure on this point but I suspect that the number is always greater than 0 and always less than 5.</p>
<p>This means that the integer part is never going to be 5 and will sometimes be 0. As you can see in the results, we are therefore generating a random number between 0 and 4. This can be quite easily fixed by shifting the range of possible values to between 1 and 5. We do this simply by adding 1 to the result.</p>
<pclass="inset">say "Result is : ", int (rand 5) +1;</p>
<p>Note that it is important to use the brackets around rand 5 because we want to generate a number between 0 and 4 and then add 1 to it. If we omit them like this</p>
<pclass="inset">say "Result is : ", int rand 5 +1;</p>
<p>the result would be a random number between 0 and 5 so this would simply be the equivalent of</p>
<pclass="inset">say "Result is : ", int rand 6;</p>
<p>This is increasing the range by 1 rather than shifting it up by 1.</p>
<p>Bear in mind that if the assumption that a random number between 0 and 5 is not inclusive turns out to be false, there is a possibility that when we try to get a random number between 1 and 5, we might actually generate a number of 6. However, this can only happen when rand produces a number which if output, would be</p>
<pclass="inset">Result is : 5.0000000000000</p>
<p>which is, obviously, so unlikely that you could probably safely ignore it. Unless the documentation explicitly mentions this, there is probably no way to be completely sure on this point since the chances of generating the number at random is negligible.</p>
<p>However, I guess that if you need to be 100% sure and it isn’t mentioned in the documentation, you could always add some code that causes such an extreme case to be ignored.</p>
<p>As with the string functions, documentation for the numeric functions can be found at <ahref="https://perldoc.perl.org/functions">https://perldoc.perl.org/functions</a>.</p>
<p>Actually, we can use this to check on the rand function at the above site by clicking on its name in the list. This includes the following</p>
<p><i>Returns a random fractional number greater than or equal to 0 and less than the value of EXPR. (EXPR should be positive.) If EXPR is omitted, the value 1 is used. Currently EXPR with the value 0 is also special-cased as 1 (this was undocumented before Perl 5.8.0 and is subject to change in future versions of Perl). Automatically calls srand unless srand has already been called. See also srand.</i></p>
<p>Note that EXPR represents the value we pass to rand and it is stated that the returned value is less than EXPR so that certainly suggests that our assumption was correct.<p>
<h2class="sectiontitle">List and Array Functions</h2>
<p>Figure 147 shows a script that creates and initialises an array and then outputs it.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # array.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = qw( one two three four five );
8. say foreach @a;</pre>
<pclass="caption">Figure 147 - array.pl, a simple demonstration of creating and outputting an array</p>
<p>The output when we run this is</p>
<preclass="inset">
one
two
three
four
five</pre>
<h2class="subsectiontitle">The grep Function</h2>
<p>The code shown in figure 147 uses a foreach loop to access each element of the array in turn. However, we may want to access certain elements based on some pattern and we can do that with grep.</p>
<p>The grep function searches through the elements of a list or an array and returns the elements that match the regex given to grep as a pattern. For example, let’s say we only want to output the elements of the array that include the letter t. We can use the foreach loop to return each element of the array in turn, and grep to test them against the regex.</p>
<pclass="inset">say foreach grep /t/, @a;</p>
<p>The output from running this is</p>
<preclass="inset">
two
three</pre>
<p>We can also invert this by using a negative expression by putting a ! in front of the pattern.</p>
<pclass="inset">say foreach grep !/t/, @a;</p>
<p>This will match any element that does not contain the letter t so our output becomes</p>
<preclass="inset">
one
four
five </pre>
<p>We can also use grep with a block of code. This alternative syntax gives us the opportunity to do some more processing. In this example</p>
<pclass="inset">say foreach grep { !/t/ } @a;</p>
<p>we use the block syntax without doing any additional processing, so the result is the same and we get the output<p>
<preclass="inset">
one
four
five </pre>
<h2class="subsectiontitle">The map Function</h2>
<p>Note that with grep, we can locate elements in the array that match our pattern, but this does not change those elements. To do that, we would use the map function. This is demonstrated with the code in figure 148.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # array.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = qw(1,2,3,4,5 );
8. say foreach map { $_ * 7 } @a;</pre>
<pclass="caption">Figure 148 - using the map function to modify the contents of the array</p>
<p>Here, we have changed the array to contain integers rather than strings. In the foreach loop, on each iteration an element is being read from the array and is represented by the default variable in map’s code block. The value is being multiplied by 7 and the output from this is</p>
<preclass="inset">
7
14
21
28
35</pre>
<p>Note that although we have modified the elements of the array, the original array is unchanged. Actually, map copies the amended values to a new list or array but in this example, we don’t make any reference to that list. However, we can add a line to the end of our code to output the contents of @a after the code the loop containing the map function has terminated and this gives us the output.</p>
<preclass="inset">
7
14
21
28
35
1
2
3
4
5</pre>
<p>So we can see that the original array is unchanged.</p>
<p>We saw that this type of block syntax was optional with grep. This is also the case with map so we can also use the comma syntax as follows.</p>
<pclass="inset">say foreach map $_ * 7, @a;</p>
<p>This has exactly the same effect. Interestingly, the course video suggests that the comma syntax is preferred with grep, but the block syntax is preferred with map. Personally, I think I would prefer the block syntax in either case. In particular, this is because there may be some situations where you have to use the block syntax, such as when additional processing is required.</p>
<h2class="subsectiontitle">The join Function</h2>
<p>Essentially, the join function takes the elements of a list and combines them to produce a single string separated by whatever you specify for that purpose.</p>
<p>Again, we will use a complete script to demonstrate this as shown in figure 149.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # array.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = qw (one two three four five );
8. say join ':', @a;</pre>
<pclass="caption">Figure 149 - demonstrating the join function</p>
<p>We have reverted to the original values for the array here. Note that we don’t have a foreach loop because of the fact that the function returns a single string rather than a list.</p>
<p>Two arguments have been passed to join, the first being the separator and the second being the list we want to convert. When we run this, the output is</p>
<pclass="inset">one:two:three:four:five</p>
<p>We used a colon as the separator without any spaces but we can use anything there such as</p>
<preclass="inset">
say join ', ', @a;
say join ' : ', @a;
say join ' -- ', @a;
say join ' fee foo fii fum ', @a;
</pre>
<p>In each case, the output is what you would expect. For instance, with the last example, our output would be</p>
<pclass="inset">one fee foo fii fum two fee foo fii fum three fee foo fii fum four fee foo fii fum five</p>
<p>You might think of as the split function as being the opposite of the join function. The code demonstrating this is shown in figure 150.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # array.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my $string = "This is a lot of words";
8. my @a = split /\s+/, $string;
9. say foreach @a;</pre>
<pclass="caption">Figure 150 - demonstrating the split function</p>
<p>This time, we are starting with a string on line 7. On line 8, we are creating an array called @a. The split function takes two arguments, a separator, or what we expect to be the individual items we want to extract from the string to be separated by.</p>
<p>In this example, the string is simply a series of words in the normal format for a sentence, so the words are separated by a space.</p>
<p>The separator is actually a regex which specifies a space followed by a plus sign. The plus sign, in this context, means one or more instances of the preceding character, in other words, one or more spaces. If we run this, the output is</p>
<preclass="inset">
This
is
a
lot
of
words</pre>
<p>We could have omitted the plus sign in this case, because there are only single spaces between the words. If we do that and run the code again, the output is exactly the same.</p>
<p>If we change our string to</p>
<pclass="inset">my $string = "This is a lot of words";</p>
<p>where we have, essentially, a random number of spaces between the words and run the code with this split statement</p>
<pclass="inset">my @a = split /\s+/, $string;</p>
<p>the output is, again, the same. However, if we omit the plus sign giving</p>
<pclass="inset">my @a = split /\s/, $string;</p>
<p>the number of spaces is now significant. As a result, if we execute the code with a random number of spaces between the words, the output will be</p>
<preclass="inset">
This
is
a
lot
of
words</pre>
<p>The reason we see this is that because we have multiple spaces, but we are using a single space as the separator, some of the elements in the array we are created are single spaces.</p>
<p>This can be thought of as being a string function as well as a list or function. We actually did cover it on the section dealing with string functions where it was noted that it will produce a different result, depending on whether it is used as a string or a list function.</p>
<p>As a string function, it takes the string passed to it and returns the same string reversed. In a list context, it takes a list and returns a new list with the elements reversed. This is demonstrated with the code shown in figure 151.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # array.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my @a = qw ( one two three four five );
8. say foreach reverse @a;</pre>
<pclass="caption">Figure 151 - demonstrating the reverse function in a list context</p>
<p>We have our list on line 7. On line 8, we are using a foreach loop to output the list we get by applying reverse to the list on line 7 so, so the output is</p>
<preclass="inset">
five
four
three
two
one</pre>
<p>Once again, we can find the documentation for these functions at</p>
24. my $now_string = strftime("%A, %B %d, %Y, %H:%M:%S", localtime($t));
25. say $now_string;</pre>
<pclass="caption">Figure 152 - demonstrating the use of the time functions</p>
<p>The output we get from running this code is</p>
<preclass="inset">
1607854966
Sun Dec 13 10:22:46 2020
2020-12-13 10:22:46
Sunday, December 13, 2020, 10:22:46 </pre>
<p>so, let’s examine what is happening here. On line 7, we create a variable called $t. The time() function returns the time in seconds since 1 January 1970, epoch time.</p>
<p>On line 8, this is the output and this gives us the value on the first line of output.</p>
<p>On line 10, we create another variable called $timestring and this is initialised by the value returned by the localtime() function. This function takes the output of the time function ($t, in this case) and converts it to a 9 element list in local time.</p>
<p>This is output on line 11 and is our second line of output.</p>
<p>Line 13 looks a bit weird, it looks like we are creating a list but it isn’t named and we haven’t created a reference to it. I guess that we are creating an anonymous list which can then be accessed with the default variable.</p>
<p>In any case, the list holds the 9 elements of the list returned by the localtime function. Note that the elements are all scalar names and I assume that these are the recognised names of the variables used in the localtime function.</p>
<p>Actually, the course video states that we are using localtime in a scalar context here, so it is returning a string rather than a list. However, it seems that each 'character' is actually an element from localtime. This impression is supported by the fact that we can access each element in the foreach loop.</p>
<p>Line 14 adds a 1 to the month. This is because the months are counted from 0 up to 11 hence, although it is December, it is month number 11 in Perl. This corrects it.</p>
<p>The date is correct (at time of writing) but note that in our own</p>
<p>Line 15 is adding 1900 to the year. This is because the year actually starts counting at 1900 so a value of 0 represents 1900, 50 would represent 1950, 120 would represent 2020 and so on so adding 1900 to this gives us the current year in the correct format.</p>
<p>Between lines 17 and 20, we have a loop that is iterating through the 6 elements of the list in brackets and adding a 0 to the front where the value is less than 10. I will come back to this in a second.</p>
<p>On line 21, we output a string which contains the six variables with the first group of three (the date) separated by hyphens and the second group of three (the time) separated by colons. There is also a space between the date and the time and this gives us the nicely formatted output we see on the third line output. I will repeat that for convenience.</p>
<pclass="inset">2020-11-13 10:22:46</p>
<p>Now, none of these elements have a value of less than 10, so the foreach loop between lines 18 and 20 had no effect. On the other hand, if the date had been, let’s say the 1st of December 2020 and the time was 9:30 am, without the loop to tidy it, the output would look like this</p>
<pclass="inset">2020-12-1 9:30:00</p>
<p>With the loop, the output would look like this</p>
<pclass="inset">2020-12-01 09:30:00</p>
<p>So the purpose of the loop is simply to add the leading zeroes to make the output slightly more readable.</p>
<p>Another point to note here is that the second line of the output was obtained simply by outputting $timestring which you will recall had been set to the local time (that is, the result of executing the localtime function) and it gives the output in this strange format where we day, date, time and then year. This is a standard format and this is why we created our own format, in effect, on lines 17 to 20.</p>
<p>This gives us control over how we produce the output and line 21 is where we are formatting the output so that we have date followed by time (adding leading 0s where appropriate).</p>
<p>There are a couple of points of interest regarding the above. Firstly, the epoch time mentioned which is inherited from the underlying C library has an interesting property. That is, it is fairly easy to do math with it.</p>
<p>For example, as was noted, time() returns the current time in this format and it is a large integer. This means that we can do some maths with it. As an example, today is the 13th of December 2020 so that is the date returned by epoch time (although, recall, it is returning this value in seconds so it would be more accurate to say that the returned value represents some time, to the second, on that date.</p>
<p>Let’s say we want to return the date in four years time. We can do that with the following bit of arithmetic.</p>
<p>This is the number of seconds in a day (60 seconds multiplied by 60 minutes multiplied by 24 hours multiplied by 365 days multiplied by 4 years. We also add 60 seconds multiplied by 60 minutes multiplied by 24 hours (the number of seconds in a year) and this accounts for an extra day for a leap year.</p>
<p>We know, of course, that there will be exactly one leap day in the next year, but this could get a little messy if we want the date, say, 10 years in the future where this is less clear.</p>
<p>Another approach would be to give the number of days in a year as 365.25 which is not precise but is perfect for this calculation. However, this might be problematic because the result will be an integer if the number of years is a multiple of 4, but not otherwise.</p>
<p>If we rerun our code with this addition and assume 365 days in a year, we get the following output</p>
<preclass="inset">
1734088032
Fri Dec 13 11:07:12 2024
2024-12-13 11:07:12
Friday, December 13, 2024, 11:07:12</pre>
<p>Bear in mind the fact that the value of $t is used in the localtime function so the output on the lines following the first line of output represent the same date and time in more readable format.</p>
<p>If we run this again, but with the number of years set to 3, the output we get is</p>
<preclass="inset">
1702552249
Thu Dec 14 11:10:49 2023
2023-12-14 11:10:49
Thursday, December 14, 2023, 11:10:49</pre>
<p>This is not surprising since there is no leap day in the next three years. As of the 13th of December 2020, the next leap day is a little over three years and two months away.</p>
<p>Similarly, we would be a day out for the same reason if we calculated the date 1 or 2 years from today.</p>
<p>If we run this again, but assume 365.25 days in a year and calculate the date three years from now, we get</p>
<preclass="inset">
1702530812
Thu Dec 14 05:13:32 2023
2023-11-14 05:13:32
Thursday, December 14, 2023, 05:13:32</pre>
<p>Bit if we use this method to calculate the date two years from now, the output is</p>
<preclass="inset">
2302125302
Sat Dec 13 23:15:02 2042
2042-11-13 23:15:02
Saturday, December 13, 2042, 23:15:02</pre>
<p>The reason for this discrepancy is that it is currently a little after 11 am so one whole quarter of the day has already elapsed. When we assume an additional quarter day, over three years, we are adding three quarters of a day which takes us over into the next day.</p>
<p>Over two years, we are only adding two additional quarters, so this doesn’t take us over to the next day. As a result, we get the correct date.</pre>
<p>Similarly, adding an extra quarter day would still give us the correct date. However, we would never get the correct time. Note that the date we calculated for two years from now also gives a time of around 11:15 pm (that is, 12 hours or half a day from now).</p>
<p>To summarise both methods, the first, as given in the course videos, will only give you an accurate answer if there is exactly one lead day in the period covered. It could be adapted but it does assume that you will know this in advance, which would likely mean you didn’t really need to do the calculation.</p>
<p>The second method (my own) works perfectly for any number of years that is divisibly by 4 and may give you the correct date (but not time) depending on the time of day when you run the calculation.</p>
<p>This seems to suggest that there is no easy way to calculate the date in this way, but it does illustrate the point that it is easy to do maths with the date in this format, but also that you should be wary of being over-reliant on it because, whichever method you use, you may not always get the exact answer.</p>
<p>Another interesting point relates to localtime. In our example, we create a variable to represent the current epoch time ($t) and used this in the variable we used to represent localtime ($timestring). We don't need to provide an argument to localtime. If it were omitted, localtime would simply return the current time. In this example, this may well have meant that $t and $timestring would represent exactly the same time since execution time is probably less than 1 second.</p>
<p>However, using the variable $t with localtime guarantees we get the same result for both. Of course, we may not want the same result in which case we would omit and localtime would return the precise current time when it was executed. That's probably an unlikely scenario since, if we wanted the current time at two distinct points in our code, we would probably use the same function both times. For example, if we want to know the number of seconds to have elapsed between the two, we would probably use the time function both times and then subtract the first from the second.</p>
<p>Obviously, this type of technique is only likely to be useful if the elapsed time is meaningful in human terms, that is, it suitable to measure it in seconds rather than micro or milliseconds.<p>
<p>Going back to the code in figure 152, there were a few lines at the end that I have ignored until now because the other parts of the code were inter-related whereas this is something else. The lines were 23 to 25 in figure 152, but I have reproduced them in figure 153 for convenience.</p>
<preclass="inset">
1. use POSIX qw( strftime );
2. my $now_string = strftime("%A, %B %d, %Y, %H:%M:%S", localtime($t));
<p>This is the POSIX version of strftime and it’s an ISO standard which means there is plenty of documentation for it on the Internet and you will also see it, I imagine, in places other than inside a Perl script.</p>
<p>The Linux man pages describe the strftime function at <ahref="https://man7.org/linux/man-pages/man3/strftime.3.html">https://man7.org/linux/man-pages/man3/strftime.3.html</a>. This includes a list of all the tokens you can insert into a string to format the output of date and time as required.</p>
<p>As a reminder, the output we get from this, which was the last line of output when we ran the code in figure 152, is<p>
<pclass="inset">Thursday, December 14, 2023, 05:13:32</p>
<p>The tokens used in this string are as follows:</p>
<preclass="inset">
%A - this is the day of the week in long format (Thursday). A lower case token, that is, %a, would give the abbreviated version of the day of the week (Thu).
%B - the month. Again, the upper case token gives us the full version (December) and a lower case token, %b, would give us the abbreviated version (Dec).
%d - the day of the month (14).
%Y - the year. There is also a long and abbreviated version here. %Y gives us the long version, 2020 and %y would give us the shortened two digit version, 20.
%H - the hour.
%M - minutes.
%S - seconds.
</pre>
<p>You may have noticed that the strftime takes two arguments, the string to be formatted and a time value (given in this example by localtime($t). I suspected that it might be possible to just use the current time by omitting the time value, but this gives an error. However, you can have strftime output the current time when that line of code executes by omitting the $t, so you would use just localtime().</p>
<p>We can also use gmtime (GMT time) rather than localtime() which may be useful if you are saving timescales in logs and so on where you want the time to be independent of your local time zone. This might be useful, for instance, if you have servers in different time zones and you want them to be logging the same times.</p>
<p>Since I am located in the UK, both gmtime and localtime give the same result.</p>
<h1class="sectiontitle">The undef Function</h1>
<p>There is a difference between setting the value of a variable to something like 0 or the empty string ''. If a variable doesn't have a value, its value is said to be undefined so if we create a variable without giving it a value, it has an undefined (undef) value.</p>
<p>If a variable does have a value and we want to remove it, we can do that by setting it's value to undefined using the undef function.</p>
<p>Figure 154 shows an example of this.</p>
<preclass="inset">
1. #!/usr/bin/perl
2. # undef.pl by Bill Weinman <http://bw.org/contact/>
3.
4. use 5.28.0;
5. use warnings;
6.
7. my $x;
8.
9. if (defined $x) {
10. say "x is $x";
11. } else {
12. say "x is not defined";
13. } </pre>
<pclass="caption">Figure 154 - demonstrating the use of the undef function</p>
<p>We have created a variable, $x on line 7, but we are not giving it a value.</p>
<p>On line 6, we have a conditional statement. The condition uses the defined function which takes a variable as an argument and returns true if the variable is defined and false if it is not.</p>
<p>If the condition is true, the value of $x is output and if it isn't, we output a message stating that $x is not defined. We can run this and the output is</p>
<pclass="inset">x is not defined</p>
<p>This is not the same as saying that $x does not exist. If we comment out line 7 and rerun the code, we won't see output stating that $x is undefined. Instead, we will see error messages such as</p>
<preclass="inset">
Global symbol "$x" requires explicit package name (did you forget to declare "my $x"?) at undef.pl line 9.
Global symbol "$x" requires explicit package name (did you forget to declare "my $x"?) at undef.pl line 10.
Execution of undef.pl aborted due to compilation errors.</pre>
<p>So, the output stating that $x is not defined can be interpreted as $x does exist (it is recognised as a variable defined in the script and with the appropriate scope) but it doesn't have any value assigned to it. Actually, undef is the default value for a variable so we won't see, for instance, numbers having a default value of 0 or a string with a default value of the empty string.</p>
<p>If we reinstate line 7 and give $x a value, let's say 42</p>
<pclass="inset">my $x=42;</p>
<p>we can run this again, and this time the output is</p>
<pclass="inset">x is 42</p>
<p>Now that $x has a value, it is no longer undefined. However, we can remove the value and set it to undefined again using the undef function. For example, we might add this line immediately after line 7.</p>
<pclass="inset">$x = undef;</p>
<p>If we run the code again, and bear in mind the fact that we did initialise $x with a value of 42, we get the output</p>
<pclass="inset">x is not defined</p>
<p>Note that we didn't pass $x to the undef function, however, we can do that and in Perl, this is probably more common so we could have used</p>