Yeah, if you set $/ as a reference to a number, apparently it treats the input as comprising fixed-length records that don't use delimiters to separate them, where the length is the value of the number obtained by dereferencing $/. Except when that number is zero; then it's a deprecated way to do the same thing as setting it to undef, so that the entire input is regarded as a single record.
They did? Can you find the question? Was this about splitting files for archival purposes? I expect that using $/ = $n for that (where $n is the number of characters) would work if you have enough memory, but be extremely inefficient, for large $n.
but mightn't that be useful in some situations? I don't think it's clear what OP wanted to do, apart from understand why they weren't getting the output they expected
The six characters in "baz\nqu" (where \n indicates a newline) are interpreted as a record, so it considers qu to appear at the end of a record, and ux to appear at the beginning of a record.
It's not using actual record separators, in the sense of text that indicates when a record ends, at all.
It has to use references to numbers, btw, because Perl tries hard to let you use numbers and strings interchangeably, and if it can interpret something as a string where a string is usually expected, it will. For example, setting $/ to 0 would have the same effect as setting it to '0', like I did there.
This is also why the string '0', which is the canonical representation of the number 0 (that the Perl interpreter produces when you use the number 0 somewhere a string is expected), has to be treated as false, even though it s a non-empty string.
It is also why separate operators are needed to add numbers and concatenate strings: you're not supposed to have to think about whether you're using a string or a number. Similarly, numbers are officially 64-bit floating point values--even when they come from literals that looks like integers, like 3--so you don't have to think about whether they're integers or floating point.
(Internally, I believe Perl stores integer and floating point representations separately--as an optimization--but the built-in numeric type is double-precision floating point, as in JavaScript and AWK. There are pragmas that change what literals like 3 mean, though. For example, if you write use bigint; then 3 is a Math::BigInt.)
What kind of thing Math::BigInt is? It's sort of hard to know what that is if you don't know about references, and also about how Perl does OOP with blessed references. But basically it's an object. You can see its structure in reply:
Internally it is represented by storing a sign of '+' or '-' and a value consisting of a list of numbers that are in effect digit groups. Most languages have a type like this, though in some it's just the regular integer data type.
In ordinary notation for writing numbers in base 10, it's common to write numbers with digit groups. For example, if one uses the comma as a thousands separator, one might write the result of raising 2 to the 32nd power as: "4,294,967,296" Similarly, if one must span binary digits of a number across multiple items in a collection because they won't fit in a single item, this is conceptually what is being done. In most implementations, binary is still being used, and the groups are 32 or 64 bits.
In Perl, with Math::BigInt, however, they are actual groups of decimal digits (stored numerically, not as strings). They are groups of 9 digits instead of 3. So if you have a big number you want to represent, and you divide by a billion (short scale), the remainder is the first item in the value list in Math::BigInt.
And the integer portion of the quotient is used for the rest of the groups.
@Zanna Cool. I'm going afk but just for a couple minutes! Btw, did you get a chance to read all the stuff from above, that you'd said you were planning to read, and which I'd said you maybe didn't have to read? It's fine if you did not, but if you did then I can keep that in mind.
When you're not reading from a filehandle in the expression you are using as its condition, while works as you'd expect from other languages. It evaluates its condition, if the condition is false it stops, if it's true then it runs a statement or block of code, then evaluates its condition again, and so forth. This is the same way while works in shell scripting, for example. Perl even has until (as do Bourne-style shells), and it's less often used than while (as in Bourne-style shells).
That script uses the special variable $|, which causes the output buffer to be flushed automatically with each print. When you output to a terminal this is typically unnecessary as then standard output is line buffered. In this case it is necessary because it is important that the user see each string printed at the time that print is called, even though they are not terminated by newlines.
You may recall that my can appear wherever. Here's a script that tries to print all the command-line arguments. It's broken, though; sometimes it stops early:
#!/usr/bin/env perl
use strict;
use warnings;
while (my $arg = shift @ARGV) {
print "$arg\n";
}
@ARGV is an array of the command line arguments. It does not include the progran name itself, which is stored in the special variable $0. That's confusing because the other numbered special variables, $1, $2, and so forth, store numbered backreferences (they are populated during regex matching).
shift is a built-in function that returns the first element of an array, while shifting all the others downward by one, so that the element originally indexed 0 is returned, the element originally indexed 1 then has the index 0, the element originally indexed 2 then has the index 1, and so forth.
I called my script shift (which perhaps I shouldn't have, since shift is a shell builtin, but I'm running it as ./shift anyway). It might seem like it works properly:
When its array argument is empty, shift returns undef, which evaluates as false. But there are other scalars that evaluate as false, including q{} (which is just another way to write '') and q{0} (which is just another way to write '0').
You can instead write:
#!/usr/bin/env perl
use strict;
use warnings;
while (defined (my $arg = shift @ARGV)) {
print "$arg\n";
}
ek@Io:~/pl$ ./shift2 foo bar 0 baz '' quux
foo
bar
0
baz
quux
Of course, if that's all the script is going to do, then it's just a silly way to write:
#!/usr/bin/env perl
use strict;
use warnings;
for my $arg (@ARGV) {
print "$arg\n";
}
ek@Io:~/pl$ ./noshift foo bar 0 baz '' quux
foo
bar
0
baz
quux
That for loop can be written shorter if you want. For example, it can be written:
There are cases where it makes sense to write things likethat. Probably not that exact code. But it can be handy to shift off an unknown number of elements from an array, when you're going to use the array later and only want it to contain the subsequent elements.
Here's another example of while that works fine, though there is also a way to replace it with for:
#!/usr/bin/env perl
use strict;
use warnings;
for my $arg (@ARGV) {
my $m = 0;
my $n = 1;
($m, $n) = ($n, $m + $n) while $arg-- > 0;
print "$m\n";
}
You will notice that this is a very inefficient way to generate Fibonacci numbers, since it repeats all the work for each of its potentially many command-line arguments. There are much faster methods. However, this is still far faster than the naive recursive solution.
Can you see how the while there could be replaced with for?
#!/usr/bin/env perl
use strict;
use warnings;
use bigint;
for my $arg (@ARGV) {
my $m = 0;
my $n = 1;
($m, $n) = ($n, $m + $n) while $arg-- > 0;
print "$m\n";
}
@Zanna Oh. That works the same as -- in an arithmetic expression (with (())) in Bash. So, ++ increments and -- decrements, which is to say that it modifies its value by increasing it by 1 (for ++) or decreasing it by 1 (for --). However, you have to distinguish between pre-incrementation and post-incrementation with ++, and pre-incrementation and post-incrementation with --. Are you familiar with concepts of "side effects" and "return value"?
(I'm sure you're familiar with return value, but I don't know about side effects.)
Using ++ as my example (but this all applies to -- the same way), the side effect of ++x and x++ are the same. But the return values are different. ++x returns the value after it was changed, while x++ returns the old value.
Speaking generally and about any programming language: When you evaluate an expression, the return value is what the expression gets "replaced" with: it is the value of the expression. Side effects are any changes to the state of the system that occur while evaluating it. Expressions may have side effects or return value, or both, or neither (but they they are not very useful). Some expressions are evaluated manly for their side effects, while others are evaluated mainly for their return value.
Yes. When the return value is unused, it doesn't make a difference whether you use prefix or postfix incrementation/decrementation. As another example, you have seen, and probably used, code like this in Bash, where n has been assigned a number:
for ((i = 1; i <= n; ++i)); do printf '%d\n' "$i"; done
That's what I mean by "C-style for loop." It is not the main form of the for loop in either Bash or Perl, but both have it. In Perl, as in C, there is of course only one pair of enclosing parentheses around the semicolon-delimited expressions, because the (()) notation is shell-specific, where it indicates that arithmetic evaluation is happening. Note that the C-style for loop in shell scripting is a "Bash-ism." Though it is supported by some other Bourne-style shells, it is not POSIX.
It feels like one should be able to use this, but it does not work in Bash, because brace expansion in Bash always happens before parameter expansion, and never after:
We printed it, in each iteration of the loop. Correct. Sorry. The return value was discarded, which is why it didn't matter whether we used the prefix or postfix form, just as you say. We later accessed the variable that was modified as a side effect of evaluating ++i, printing it in each iteration of the loop.
Yeah sorry, what we printed in the loop was not even always the same as the return value of the incrementation expression. In the first iteration of the loop, that expression had never been evaluated. In subsequent iterations, when we used ++i, what we printed happened to be the same as the return value, which we discarded.
When we used i++, what we printed was not the same as the return value of that expression (which we discarded), because the return value of that expression was instead the value printed in the immediately preceding iteraton. In neither case was the return value itself ever used.
@Zanna No problem! I should disclose that the term "return value" is often used just to mean the value returned from an actual function (some languages have a syntactic construct called a function) or operator, so it may be better to call it the value of the expression, and the return value of the ++ and -- operator. However, that also occasionally risks misinterpretation, as there is more than one kind of thing that is sometimes considered to constitute the value of an expression.
The TL;DR of it is that, if you don't fear being misunderstood for another reason, it may be better to talk about an expression's value and side effects rather than as its return value and side effects. Strictly speaking, return value pertains to functions and operators.
@Zanna Since we're on this topic, are you familiar with lvalues and rvalues?
As an example of side effects, this is unnecessarily complicated: changing the value of its left operand is a side effect of the = operator. (It also has a return value; it is the value it was changed to, so you can write stuff like $x = $y = $z = 3. Similarly, with arithmetic evaluation in Bash, you can write ((x = y = z = 3)).) You don't need an array to observe the side effects (and return value) of =. However, I think subscripting an array helps make lvalues vs. rvalues intuitive.
So, first, do you understand what $arr[5] means and why it is used to mean that? Specifically, do you understand why this means the sixth element of the array @arr, and also why it is accessing a scalar that is part of the array called @arr and not accessing a scalar called $arr?
None of that, yet, is about lvalues vs. rvalues. It just occurred to me that you may not know how array accesses work in Perl.
ok, so, can we say that there is a scalar that can be called $arr[5] which means the 6th element (counting from zero) of... @arr? Could there be another kind of thing called $arr[5] that isn't the 6th element of an array?
(I will have to go to bed soon, because I can't manage to stay asleep longer if I stay up late)
@Zanna Yes, we can absolutely say that. That is true. As for whether there can be another thing called $arr[5] that isn't the 6th element of an array, no. I mean, it is possible for $arr[5] not to exist, and then if you try to read the sixth element of @arr then you get undef, but besides that, no. However, it feels like $arr[5] should have something to do with the scalar called $arr. It doesn't, though. That scalar can exist; it is not referred to by the expression $arr[5].
@Zanna I definitely do not want to keep you from that. We can totally continue this later!
Perl 5, which is what "Perl" has meant for some years now when people use it as the name of a language (rather than as the name of a family of languages), does not have sigil invariance. Perl 6 does have sigil invariance, and you would just write @arr[5], never $arr[5]. But in Perl 5, the preferred way is $arr[5]. Notice that you've actually been seeing this notation whenever you use reply, because it maintains a @res array of each of your results.
@EliahKagan that's OK then, I'm not worried about the hypothetical scalar $arr because it's not the same as $arr[5] (which is a scalar, so that's why it's $)
So, in that example, I assigned to $arr[5]. The expression $arr[5] uses the array subscripting operator, []. Its first operand was, uh, @arr, and its second operand was the index 5. That expression did not itself have any side effects. We used it only for its return value, which we passed as the left-hand operand to the = operator to assign to it. What was the return value of $arr[5]?
@EliahKagan I'm glad you've mentioned this, because I just kind of got an inkling of that idea when I was copying that, because while reading I got momentarily confused between $arr[n] and $res[n]
Right, that was the value of $arr[5]. But was that what we assigned to?
9> 105 = 55;
Can't modify constant item in scalar assignment at reply input line 1, at EOF
BEGIN not safe after errors--compilation aborted at reply input line 7.
Yeah, there was no one hundred fifth regex capture defined when you ran that. :)
10> my $x = 3; $res[7] = 3
11> ++$x; $res[8] = 4
The ++ operator is surely using the value 3. It can't get 4 without it. But it is doing more than that.
Or just in:
12> $x = 10;
$res[9] = 10
So there is more than one kind of thing that "value" can mean. If the variable $x is holding the value 10, we might use $x for what it stores, 10. Or we might use it to talk about a place where something can be stored.
Consider this: suppose separate variables $x and $y hold the same value. When can $x and $y be used interchangeably (so you can replace an occurrence of one with an occurrence of the other in any expression, without changing what the program does), and when can't they?
To tie things up a bit since you said you want to sleep: when an expression can be replaced by the value it holds without changing anything, we say we are using it as an rvalue, and when it cannot, we say we are using it as an lvalue. So $x = $y uses $y as an rvalue and $x as an lvalue. This is how "lvalue" and "rvalue" got their names: from the left and right sides of assignment.
Note, however, that it is not true in all languages to say that an expression is an lvalue if and only if it can be assigned to. This was true in BCPL, which I think is what those terms are from. It is not true in C. (People sometimes say that lvalues in C are exactly those expressions whose address may be taken. That is also not true.) Conceptually, an expression is an lvalue if it refers to something that is somewhere, while an rvalue is just a value.
So ++$x uses $x as an rvalue to obtain the number that one will be added to, and uses it as an lvalue to write the result.
(Of course, this may be optimized to an operation that is implemented differently -- when you have a number stored in an integer register in the CPU, incrementing it is usually a unary operation where only the register that stores it has to be identified -- but conceptually that is what is happening. Really, the description is probably exactly what is happening, as $x likely has to be copied to a register, incremented, and then copied back to wherever it was stored in main RAM or in cache.)
So, one way of using the terms "lvalue" and "rvalue" is to say that anything that "has identity" (i.e., is somewhere, so there is some data in memory that is that thing, in a way that an identical pattern of bytes elsewhere is still not that thing) is an lvalue, not an rvalue, but that lvalues can be used as rvalues by converting to them, decaying to them, etc. (the terminology varies).
This is basically what those terms officially mean in C and C++ prior to C++11 when the value categories were revised to support move semantics. But another way to use the terms "lvalue" and "rvalue," which I have seen, is to say that variables--and any expression that identifies a storage location--each have an lvalue and an rvalue, and that $x = $y assigns the rvalue of $y to the lvalue of $x.
In the foregoing, I have tried to use the terms in a way that will only slightly offend people who prefer either of those usages, which is why I have used the phrases "as an lvalue" and "as an rvalue."
@Zanna Well, I would say that = in algebra doesn't mean assignment.
One way that ++$i is often explained is to say that it means $i += 1. And one way that $i += 1 is often explained is to say that it means $i = $i + 1, except that $i is only evaluated once. It can matter how many times you evaluate something--probably not $i, but a more complex expression, though in Perl it may not look more complex--because expressions have side effects, and those side effects can even cause subsequent return values to be different.
For example, <> in scalar context reads a line; then <>in scalar context again reads the next line.
One would never have x = x + 1 be true in algebra; not with ordinary numbers. And if it were true, it would still not be related to assignment.
I once had a conversation at a Linux meetup about education, and some non-teachers were saying that some kids just cannot get that it's perfectly normal that x = x + 1 when learning computer science, but I didn't believe that they "just can't get" it
I would argue that it is objectively bad that = is used for assignment, and that the only argument for it is that there is so much cultural inertia behind it. Some languages use := or <- to mean assignment. I consider that objectively better. Using = for assignment tells the lie that changing the value of an expression is an ordinary thing that should not surprise people. But it should surprise people; it's super-weird.
hmm agreed! I have probably caused my students to "just not get" that thing by being ultra-pedantic about not writing = when it is false
@EliahKagan happy new year to you! thanks for your patience explaining many things :) hopefully we can continue in 2018 and find out what while is doing eventually XD
@Zanna If presented as something that is true, it seems to me that it is very good that you avoid saying that x = y and that x = z when it is false that y = z. If that's what you mean.
@Zanna Well, except when reading from a filehandle, I think you know what while is doing. It's only in weird cases like while (<>) that its behavior is different because it is assigning to a variable and it stops at undef rather than at any false value. But yes, more while later! :)