About Perl's Operators

In Perl, the operator often determines the type of the operands so it is necessary to have different operators for different data types. For instance, the comparison operator is == for numeric variables and eq for string variables.

A tutorial on Perl operators can be found here and Perldoc provides descriptions of these operators here. Figure 48 provides a full list of operators and shows operator precedence.

				

Operator Precedence in Perl

1 left terms and list operators 2 left -> 3 nonassoc ++ -- 4 right ** 5 right ! ~ \ and unary + and - 6 left ~ !~ 7 left * / % x 8 left + - . 9 left << >> 10 nonassoc named unary operators 11 nonassoc < > <= >= lt gt le ge 12 nonassoc == != <=> eq ne cmp ~~ 13 left & 14 left | ^ 15 left && 16 left || // 17 nonassoc .. ... 18 right ?: 19 right = += −= ×= dump 20 left , => 21 nonassoc list operators (rightward) 22 right not 23 left and 24 left or xor

Figure 48 - Perl's operators

Basic Arithmetic Operators

The arithmetic operators in Perl work mostly as you would expect but note the following. You might have two integer variables such as $x and $y (let’s say these have values of 47 and 4 respectively). If you divide one by the other in a statement such as

say $x / $y ;

you might expect this to represent integer division which would give a result of 11. However, it isn’t and we will see a result of 11.75. This goes back to what we mentioned in the previous section about the operand determining the data type of the operators.

In fact, Perl tends to treat all numbers as floating point so there is, in fact no integer division. However, we can achieve the same result by taking the int value of the result of our division so

say int($x / $y) ;

will give us a result of 11 so the effect is the same as if we had carried out an integer division. We do have a modulo operator which is, as you may expect, %.

When working with floating point numbers, rounding errors can be a big problem in Perl and an ideal solution would involve creating a class that can handle fixed point numbers and fixed point arithmetic. CPAN does actually have modules that can do that but that’s not really the point here because we aren’t trying to get too deep into the process. At this stage, we just want to demonstrate the problem and one simple solution that doesn’t involve any arithmetic.

To do this, we have a program called dollars.pl which is shown in figure 49.

		1.	#!/usr/bin/perl
		2.	# dollars.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my $z = 1 + 10 / 100;       # one dollar and 10 cents
		8.	say "z is $z";
		9.	printf("messy: %.30f\n", $z);
		10.	 
		11.	my ($d, $c) = sanitize_dollars_cents($z);
		12.	 
		13.	printf("sanitized: d: %.30f c: %.30f\n", $d, $c);
		14.	 
		15.	printf("price is %d\.%02d\n", $d, $c);
		16.	 
		17.	# sanitize_dollars_cents( $value )
		18.	# takes one scalar number, returns a list of two values
		19.	sub sanitize_dollars_cents {
		20.	    my $value = shift || 0;
		21.	    my $d = int($value *= 100);  # total num of cents
		22.	    my $c = int($value % 100);   # cents part only
		23.	    $d = int($d / 100);          # dollars part only
		24.	 
		25.	    return ($d, $c);
		26.	}

Figure 49 - dollars.pl which is used to demonstrate floating point arithmetic (good and bad) in Perl

So here, we have a variable, $z, which we have set to a value of 1.10 - that is, it has been initialized to 1 + the result of dividing 1 by 10. Essentially, it is one and a tenth dollars!

When we display $z on line 9, which is described as messy, we are using C like print formatters to display the value of $z to 30 decimal places and the result, as we can see, is a number that is actually very slightly more than 1.10.

On line 10, we are making a call to the function called sanitize_dollars_cents. We pass the value of $z to this function and we get two values returned. Yes, that does look a little weird but for the time being we will just accept that a function in Perl can return more than one value.

In this function, we multiply $z by 100 and this is, in fact, converting a dollar amount to an amount of cents. That is, $1.10 is equal to 110 cents so we are simply multiplying 1.10 by 100 to give 110 and this is just a simple conversion from an amount in dollars to an amount in cents.

It may be worth noting that when we refer to these values as an amount in dollars or an amount in cents, this is a semantic interpretation of the values - it is the meaning we give to the numbers. To the Perl interpreter, these are nothing more than numeric values.

Inside the function, we have 4 statements (excluding the return statement) so let’s look at each one in turn.

my $value = shift || 0;

This simply retrieves the argument passed to the function and sets the value of $value to that argument. In this case, we passed the value of $z to it so $value is initialized to the value we saw in the output from line 8. It is about, but not exactly, 1.10.

The next line

my $d = int($value *= 100); # total num of cents

performs the simply conversion from a dollar value to a cent. The result of the multiplication is about 110 but the extra digits are after the decimal point so by taking the integer value, we get rid of them so $d now has a value of exactly 110.

On the next line, we are doing a sort of reversal of this conversion where we are converting the value in cents back to a value in dollars, but we are using the modulo operator. So, this will convert the value in cents to a value in dollars. That is 110 is converted to 1.10 which we can also state as 1 (dollar) remainder 10 (cents). The modulo operator returns this remainder (10) and converts it to an integer.

my $c = int($value % 100); # cents part only

he fourth line does something similar.

$d = int($d / 100); # dollars part only

In this case, we are using the division operator which is taking the value of 110, dividing it using the division operator and then taking the integer value of the result. So 110 becomes 1.10 after the division and then 1 after taking the integer value of the division result.

Note that we set the value of $d initially to the number of cents (110), then $c to the actual number of cents when we took the cents part only (10) and then $d to the result of the final calculation which works out the number of whole dollars (1).

The final line in the function

return ($d, $c);

returns the values of $d and $c which is the value, respectively in dollars and cents. So, to sum up, we passed a monetary value ($1.10) to the function and this return the same value expressed separately as dollars (1) and cents (10).

On line 13, we output both these values in the same format used when we displayed the “messy” value on line 9. That is, both values are displayed to 30 decimal places and this demonstrates the fact that the values respectively are exactly (at least to 30 places) 1 and 10.

On line 15, we print these values again but this time showing the dollars (or dollar) as a decimal value with no floating point element (in other words as an integer) followed by a dot and the cents as an 2-digit decimal value.

Why do we have to display it as a 2 digit value? It looks as though the programmer has become a little confused and is trying to display it to 2 decimal places to make sure we get 1.10 as output rather than 1.1. However, both the dollar and cent amounts are integers so we don’t need to do that. We might say that 1.1 and 1.10 are the same value, but the amount in cents is an integer and 1 and 10 are certainly not the same value. If we amend the line to

printf("price is %d\.d\n", $d, $c);

the output is exactly the same and we do see “price” displayed as 1.10.

We need to be careful if we also want to display the appropriate currency sign ($) since this has a specific meaning in Perl but we can escape it so that the line becomes

printf("price is \$%d\.d\n", $d, $c);

and now we see the last line of the output as

price is $1.10

So this is a very simple example. In practice, you would want to write a class to handle this type of arithmetic or download a module from CPAN. We will cover that when we look at modules later, and we will also look in more detail at functions and how they return values.

As we have seen, arithmetic operators in Perl work mainly as you would expect them to but when working with financial values, as we saw in dollars.pl, you should be careful to ensure that your values are sanitized where necessary.

Compound Assignment Operators

These work exactly as they do in C and more info can be found in the perldoc here.

One thing worth pointing out is that if we compare two statements in Perl.

$x = $x + 2; # normal assignment

and

$x += 2; # compound assignment

in the statement labelled normal assignment, $x is evaluated twice but in the statement labelled compound assignment, it is evaluated once. In most cases, this won’t make any difference, but if $x represents a class or something else that can be changed by evaluating it, that can make a difference. There may, in other words, be an unintended side effect.

The compound assignment operators include the following

			+=
			-=
			*=
			/=
			%=
	

Note that they can only be used on variables in a scalar context.

The Relational Operators

Relational operators return a true or false. Recall that Perl regards things that are empty or have a value of 0 as false and everything else as true. The relational operators, in general, return 1 (the numerical value) for true and an empty string for false.

Equality Numeric String
Equal = = eq
Not Equal != ne
Comparison <=> cmp
Relational Numberic String
Less than < lt
Greater than > gt
Less than or equal <= le
Greater than or equal to >= ge

Figure 50 - a table of relational operators

Figure 50 provide a list of the relational operators. Recall that there are separate operators for strings and these are also listed here. The table was taken from the webpage of the Computer Science department at Cardiff University. As a matter of interest, their homepage is here and the University’s home page is here.

Also, bear in mind that the relational operators will return either a true value which is generally 1 for true and an empty string for false.

Logical Operators

Perl has two types of logical operators with different levels of precedence and these are used to bind logical operations according to Boolean rules.

A typical example of this is to use an operator to bind two conditions as in

if ( $a == $b and $c == d )

This type of logical operator has a very low precedence, so the comparisons are always done first. Each of these will return a true or false value and the entire expression will return true or false based on those values. In this case, it will return true if, and only if, both comparisons return true values.

Other logical operators are or, not and xor (exclusive or which will return a true value if one or the other comparison return true but not both).

Perl also has sone C like symbolic logical operators and these are listed in figure 51.

			$a && $b performs logical AND of two variables or expressions. The logical && operator checks if both variables or expressions are true.
			$a || $b performs logical OR of two variables or expressions. The logical || operator checks either a variable or expression is true.
			!$a performs logical NOT of the variable or expression. The logical ! operator inverts the value of the followed variable or expression. In the other words, it converts true to false or false to true.

Figure 51 - the C-like symbolic logical operators in Perl

The table in figure 51 is taken from a page at https://www.perltutorial.org/perl-operators/#Logical_operators.

These have a slightly higher precedence than the named operators and they are very similar, in most cases they could be used interchangeably. However, their main purpose is to provide a short-circuiting version of the logical operators. Consider the following example

my $x = $a || 57;

Here, we have an assignment operator and a logical expression. Bear in mind that the $a variable, by itself, is not a logical operation and it returns itself. Similarly, 57 is number literal so it returns 57.

Now, we are assigning the result of this logical expression to $x and we can see that the expression will return a true value. But, if it $a returns a true value, the expression is not evaluated further and the value of $a is returned. This value is then assigned to $x. If it returns a true value, which in this context means 0, the value 57 is returned and this is assigned to $x.

The overall effect of the statement is therefore to assign the value of $a to $x if $a holds a non-zero value. If $a holds a zero value, we will assign the value 57 to $x. So, in this case, 57 is a default value.

It’s also worth noting that if $a has a non-numeric value such as a string, this will still work although it may make slightly less sense. In this case, $x will take a string value if $a is a non-empty string. If $a is an empty string, $x will again take the numeric value 57.

I guess that if $a had a more complex value such as an object, the same would apply depending on whether the object had a value that returned as false (such as null).

So, the logical expression is using short circuiting.

The same applies to the other logical symbolic operators. For instance, we could use && in the above example in place of ||. If we do, && will also short-circuit but it is important to understand what is meant by this. The logical expression will try to return a value based on the whole expression, but will stop as soon as that value has been unambiguously determined. In the case of ||, if the first value returns true, the expression will return true and so there is no need to evaluate the second part.

In the case of &&, both parts must be true. This doesn’t mean that we will always evaluate the second part, but we will always evaluate it if the first part returns a true value. If the first part returns a false value, the whole expression must return a false value so we stop there.

In the context of this example, we have kind of reversed the logic. Now, $x will always take the value of 57 if $a returns any kind of true value. If $a returns a false value, for instance 0, $x will take that value. In this case, we can’t really consider 57 as a default value in case we don’t have another value to assign to $x so it’s use is probably less obvious. For this reason, we will see this type of syntax used quite commonly with || but very rarely with &&. However, there will be other circumstances where the short-circuiting property of && is very useful.

Note that if there is no true value to be returned, $x will be assigned whatever false value is looked at last. For example, let’s say we have two values, $a and $x b, both of which return some kind of false value which may be an empty string or a zero value (perhaps also a null). If we do this

my $x = $a || $b;

x will be assigned the false value form $b since both parts need to be checked to ensure that we know whether the expression evaluates to true or false. However, if we do this

my $x = $a && $b;

x will be assigned the value of $a since the expression cannot evaluate to true if the first part of the expression returns a false value.

File Test Operators

Figure 52 contains some code to check whether a file exists.

		1.	#!/usr/bin/perl
		2.	# fileop.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my $filename = 'testfile.txt';
		8.	 
		9.	if(-e $filename) {
		10.	    say 'file test returned true';    
		11.	} elsif($!) {
		12.	    say $!; 
		13.	} else {
		14.	    say 'file test returned false';
		15.	}

Figure 52 - fileop.pl which tests to see if a file exists

The file test operator is the -e on line 9. This takes one argument which can be a filename, a directory name or a file handle (which we will cover later).

The file test operator will return a true or false value depending on whether the parameter passed to it (in figure 52, this is a filename) exists or not.

When we run the code, if the filename exists, we see the output as

file test returned true

However, if it doesn’t exist, we will see the output

No such file or directory

This is because the elsif condition, which is checked if the file test operator returns a false value, is executed if $! returns a true value. You may recall, this is the special variable for the global error condition so essentially, if an error exists, the code in the elsif clause is executed and this displays the string value of the error variable.

The else clause is executed if the if condition returns a false without an error. If we change line 7 to

my $filename = '';

we will then pass the empty string to the file test operator. This will not return an error because there is no filename to test, but it returns a false value since the empty string is, by definition, false. So, in this case when we run the code the else clause will be executed.

There are a number of other file test operators, some of which will generate errors and some won’t. The most important file operators (according to perltutorial.org) are shown in figure 53.

			-r: check if the file is readable
			-w: check if the file is writable
			-x: check if the file is executable
			-o: check if the file is owned by effective uid.
			-R: check if file is readable
			-W: check if file is writable
			-X: check if file is executable
			-O: check if the file is owned by real uid.
			-e: check if the file exists.
			-z: check if the file is empty.
			-s: check if the file has nonzero size (returns size in bytes).
			-f: check if the file is a plain file.
			-d: check if the file is a directory.
			-l: check if the file is a symbolic link.
			-p: check if the file is a named pipe (FIFO): or Filehandle is a pipe.
			-S: check if the file is a socket.
			-b: check if the file is a block special file.
			-c: check if the file is a character special file.
			-t: check if the file handle is opened to a tty.
			-u: check if the file has setuid bit set.
			-g: check if the file has setgid bit set.
			-k: check if the file has sticky bit set.
			-T: check if the file is an ASCII text file (heuristic guess).
			-B: check if the file is a “binary” file (opposite of -T).

Figure 53 - the most important file test operators

Note that some of these tests will be much more common than others, particularly the tests for existence (-e), readability (-r), writeability (-w), zero size (-z), non-zero size (s), whether the file is a directory (-d) and whether the file is a plain file (-f).

Plain file is more or less exactly what it sounds like. That is, it is just an ordinary file with text or data inside it rather than a directory or a pipe.

You can find more info on pipes and also file handles on the Arizona State University website here. This is a downloadable pdf and goes into a lot of detail, particularly regarding file handles.

The perldoc website also has a list of file test operators and more information on using them here.

The Range Operator

In Perl, the Range operator returns a list of values according to some range.

		1.	#!/usr/bin/perl
		2.	# range.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	foreach my $i (1 .. 10) {
		8.	    print "$i ";
		9.	}
		10.	print "\n";

Figure 54 - range.pl demonstrating the use of the Range operator

The range operators is on line 7 and it is the two dots between the 1 and the 10. It takes two operators, a lower limit (in this case the 1) and an upper limit (the 10) and returns a list going from the upper to the lower limit. The limits themselves are in the list so in this example, the returned list is

(1, 2, 3, 4, 5, 6, 7, 8, 9, 10 )

For this reason, we could have written the same loop by changing line 7 to

foreach my $i (1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ) {

You can also use letters or even strings as the limits and Perl will do it’s best to work out what you mean and return a sensible list. For instance, if we change the range in line 7 to

foreach my $i (‘cab’ .. ‘caf’) {

the list returned is (‘cab’, ‘cac’, ‘cad’, ‘cae’, ‘caf’ )

Obviously, the range operator works with simple ranges such as 0 .. 9 or a .. z.

Sometimes, it is more convenient to use variables for the limits. For example, if we create a variable called $lower for the lower limit and another called $upper for the upper limit, we can specify the range as

($lower .. $upper)

It’s also worth noting that we can add some formatting. For example, if we wanted a list of numbers from 1 to 31, all represented by two digits - that is ( 01, 02, 03 … 31 ), we can specify the lower limit as 01 and the upper as 31. This might be useful, for instance, in a calendar app.

We can also combine ranges. Let’s say, for instance, we want a list of hexadecimal digits. We can get a list of these by specifying the range as

( 0 .. 9, ‘a’ .. ‘f’ )

Since the range operator returns a list, we can use it to initialise an array. This is demonstrated in figure 55.

		1.	#!/usr/bin/perl
		2.	# range.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my $lower=01;
		8.	my $upper=10;
		9.	my @array=($lower .. $upper);
		10.	 
		11.	say foreach @array;

Figure 55 - range.pl demonstrating the use of the range operator to initialise an array

One point to note on this is that the array is initialised, in this instance, with scalar numeric variables. So, it doesn’t retain the leading zero on the lower values and when we see the output, rather than seeing 01, 02 and son we see 1, 2 and so on. So we can’t use this method to display a list of numbers formatted with leading zeros as we saw earlier.

String Concatenation Operator

The string concatenation operator in Perl is similar to C or Java’s string concatenation operator and works, as far as I can tell, in exactly the same way. When you concatenate two strings, you get back exactly what was in the original strings. For example, if you have two strings, “Hello” and “World”, if you concatenate them you will get “HelloWorld”. Obviously, you can add a space to the end of the first word or start of the second word or even concatenate three strings with the space in the middle string.

Perl also has another similar operator, join, which combines strings but does include a space. We will see that later.

Quote Operators

If we have a statement such as

say “Hello World”;

we are passing a literal string to the say method which outputs that string. Note that the string is delimited with double quotes. We can also delimit a string with single quotes but that does not provide any string interpolation so if we add \n to the end of the line, for instance, rather than add a new line it will simply output

Hello World\n

We may want to use single quotes, in spite of this limitation if we want to include double quotes in the displayed string. For example

say ‘He said, “My name is Brian”’;

will give the output as

He said, “My name is Brian”

Perl provides another option for specifying a string in this way and that is with a quote operator. For example

say q(Hello World);

will give the same output as our original say statement. Consider the code shown in figure 56.

		1.	#!/usr/bin/perl
		2.	# say.pl by Philip Osztromok
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	say 'Hello World!\n';
		8.	say "Hello World!\n";
		9.	say q(Hello World!\n);
		10.	say qq(Hello World!\n);

Figure 56 - say.pl demonstrating the use of quote operators

The output from running this code is shown in figure 57.

		1.	Hello World!\n
		2.	Hello World!
		3.	 
		4.	Hello World!\n
		5.	Hello World!

Figure 57 - the output we get from running say.pl

Notice that the output in line 1 has printed \n at the end of the line and it did not print a newline. Bear in mind that say provides a new line character at the end of the line anyway so we do still see the next line of output on its own line.

On line 2, we don’t see \n at the end of the string, but there is a blank line after the output because we have the escaped new line character in the string which takes us to line 3 and the newline character provided by say.

So, the next output is on line 4 and this uses the q quote operator which is equivalent to single quotes which means the output is identical to that on line 1.

Line 5 uses the qq operator which us equivalent to double quotes, so we do get string interpolation here and so the output is equivalent to that on line 2.

Note the syntax of the quote word operator which is q or qq followed by a string enclosed in brackets. Actually, we can use almost any paired characters here, so we could put our string between curly braces, angle brackets, square brackets, slashes (forward or back, possibly) or pipe characters.

Consider the revised code for say.pl shown in figure 58.

		1.	#!/usr/bin/perl
		2.	# say.pl by Philip Osztromok
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	say q(Hello World!);
		8.	say qq(Hello World!);
		9.	say q{Hello World!};
		10.	say qq{Hello World!};
		11.	say q[Hello World!];
		12.	say qq[Hello World!];
		13.	say q<Hello World!>;
		14.	say qq<Hello World!>;
		15.	say q/Hello World!/;
		16.	say qq/Hello World!/;
		17.	say q\Hello World!\;
		18.	say qq\Hello World!\);
		19.	say q|Hello World!|;
		20.	say qq|Hello World!|;

Figure 58 - the revised code for say.pl checking which paired characters work with the quote operators.

This is simply a sequence of say statements using different paired characters with the q and qq quote word operators. If all of these are permissible, the code should run and we should see the line printed 14 times since we have 14 different combinations of quote operator and paired characters.

In fact, the code won’t run as is because we get an error with line 18. If I comment that out, the code does run and we get 13 copies of the statement being output.

So, the combination that doesn’t work is qq with the backslashes (\\). This makes sense because the backslash character is used for escape characters and as was mentioned before, qq provides string interpolation so it can interpret these escape characters.

The backslashes did work with the q quote operator, which doesn’t recognise escape characters and so there is no possible ambiguity.

We can sum that up by saying that we can use any of these paired characters with q and any except backslashes with qq.

Of course, there is always the possibility that any of these characters will appear in your string so you may have to choose an appropriate pair that takes this into account. The pipe character (|) is very rarely seen in a string so this tends to be a popular choice for use with the quote operators.

Another quote word operator which we have seen earlier is the quote words operator which allows us to easily create an array or list of strings such as

my @array = qw | this is a list of words |;

Again, as with q and qq, we can use any sensible pair of characters to delimit the list but we also find here that the pipe character is the one most commonly used.