📜 ⬆️ ⬇️

Unusual range operator

I must warn you that this is another article that does not contain any revelations. For those super-geeks who know the whole perldoc by heart, it will be absolutely useless, so, dear super-geeks, you can pass by and not inform you that all this is in the docks. I already know that. :-) My article is for everyone else, for those who have not mastered the whole perldoc, or mastered, but did not understand, or understood, but did not remember.

I think many people know about the so-called range operator, written as .. (two points), with which you can quickly create arrays from a set of consecutive elements. For example, the following code creates an array of 35 numbers: 3, 4, 5, ..., 37:
my @arr = 3 .. 37 ;
In addition to numbers, you can use strings: in this case, the so-called magic increment will be executed to generate array elements (for example, you can specify a range of letters: 'a' .. 'z' ).

However, the range operator can also be used in a scalar context, taking boolean expressions as operands and returning a boolean result. And here the most interesting begins, because it is an operator with a state : the result of the operation will depend not only on the values ​​of the left and right operands, but also on the call history of the given expression!

You can, of course, give a formal definition here describing the method of calculating the result for a range operator, but I personally needed to re-read this formal definition five or six times before I finally could understand the essence. Therefore, it is better to go the other way. Imagine that we are processing a file line by line, and we need to perform a certain action for some blocks in this file (for example, skip a multi-line comment). What is a block? This is almost an arbitrary set of lines, enclosed between two markers, marking the beginning and end of a block. For definiteness, we take comments in the C / C ++ style (and for simplicity, we assume that a comment and a useful command cannot be adjacent to one line). Here is an example of the code that we will process:
01: int i = 10 , j, k;
02: for (j = i; j < 2 * i; ++j) {
03: /*
04:
05: -
06: . */

07: k = j * j;
08: printf ( "Result[%d]: %d\n" , j, k);
09: /* . */
10: }
We will write code that will display all the uncommented lines of the above text. What will a C programmer do with line-by-line processing? Well, for example, it will create a variable where it will store the current state: are we inside the comment (i.e., we have read the “ /* ” marker, but have not yet met the “ */ ”), and depending on the value of this variable, output or do not display the next line on the screen, and also do not forget to change the value in a timely manner when a marker of the desired type is detected. And what will the Pearl programmer do? And he will use the range operator and write something like the following:
while ( my $line = <FILE>) {
if (($line =~ m/^\s*\/\*/) .. ($line =~ m/\*\/\s*$/)) {
# $line - , .
}
else {
# $line - , .
print $line;
}
}
What does this code do? In short, exactly what we need. The left operand of the range operator here corresponds to the beginning of the comment, the right - to the end. And the range operator itself returns true if and only if, during the execution of the code, we are in the “gap” from the operation of the left operand to the operation of the right operand. Thus, the operator fully justifies his name: he sets the logical range.
')
Now we can give a formal definition (free translation of an extract from the official documentation perlop ):
Each operator .. contains its own boolean state. It contains the value “false” as long as the left operand is false. As soon as the left operand becomes true, the range operator takes the true value and remains so until the right operand accepts the true value. AFTER this, the range operator again takes a false value.
Let us now try to sort through all the magic that turns this somewhat vague definition into the correspondence of the usual range. To do this, let us introduce ourselves as a debugger and execute the program sequentially, step by step, reading the input file line by line. For brevity, I here denote the left and right operands (Boolean expressions of correspondence to the beginning and end of a comment) as MB and ME (short for marker begin / marker end).
  1. int i = 10 , j, k;
    On this line, both MB and ME give false, therefore, the operator .. also returns false. Thus, this line is not a comment.
  2. for (j = i; j < 2 * i; ++j) {
    Similarly, MB and ME give false, so the whole expression will also be false.
  3. /*
    And here, finally, MB works; ME remains a lie. According to the definition, at this moment the range operator takes the true value, and we get the result that the read string is a comment.

  4. On this line, the expressions MB and ME again find nothing and return false. But since the operator has already switched to the true state, he will now remain in it until ME takes the true value. Thus we get the truth again, i.e. this line is a comment.
  5. -
    Here ME is still not true, so that the operator .. continues to give the truth, that is, we have not reached the end of the comment.
  6. . */
    And here, finally, it works ME . The range operator sighs and gives the last true truth, then switches back to its original state. But for this line, we still get the truth, which perfectly correlates with our ideas about the structure of multi-line comments: this line is the final, but still part of the comment, and should not be displayed according to the above TK.
  7. k = j * j;
    The freebie is over, sir. MB and ME are both false, the operator is in its initial state and returns false, so this string is not a comment.
  8. printf ( "Result[%d]: %d\n" , j, k);
    ... just like this one. For the same reasons.
  9. /* . */
    But this piece is very curious: both MB and ME work simultaneously. What does it change? Yes, in general, nothing. The operator .. , according to the definition, will return the truth, having remembered it for the future, but since the second operand is also true, the switch back to the initial state occurs immediately: the comment has begun and immediately ended.
  10. }
    This line is not caught by either MB or ME , and, since the operator has managed to switch back, he will return false here, marking this line as uncommented.
I hope that this simple example helped you figure out what was happening. In fact, inside the operator there is the very local state variable that the fictitious C-programmer we have had to enter explicitly, as well as fiddle with the management of its values.

Of course, the scope of the range operator is not at all limited to word-by-line or word-by-word processing, everything is determined only by your imagination. For example, it can be used to define some ranges in data arrays, when the boundaries are set by more complex conditions than an elementary check for more or less.

It is also worth saying here that besides the two-point operator .. there is also a three-point ( ... ). It behaves exactly the same as two-point, but with one difference: when the first operand in the initial state of the operator takes the true value, the second operand is ignored. Thus, if for our example we tried to use the operator ... , then the comment in the ninth line of the file would be considered not complete, but continuing to the end of the file (more precisely, to the line where the next comment end marker will fall, but in our file An example of such a line is simply no). As an example of use we can cite the situation when the beginning and end of the block being processed are set with the same special line. A two-point operator here would be powerless, and a three-point operator would fit perfectly.

Finally, I would like to read a little notation: please do not forget about the readability and self-comments of the code. You do not need to use the possibility of the language just because it is in it. If you really have a certain block in the sequential processing of some data, then this operator makes it possible to describe this block briefly, clearly and gracefully. But if you start shoving a range operator where neither, just because he is so original, unusual, and nobody else has it, believe me, it will not lead to anything good.

Source: https://habr.com/ru/post/87272/


All Articles