That's okay. I'll hold off on continuing what we had been talking about yesterday, which might require that you try stuff out. These are just some thoughts about code size. :)
However, I can stop typing stuff here if you think it may be distracting you or if you otherwise don't want to see it. (As far as I know, we're the only ones actively in this room at the moment.) You can let me know.
Culturally, it is encouraged when programming in Perl to use
<>
(or
<<>>
which is
more secure but less widely supported), instead of
<STDIN>
whenever it is reasonable to do so, and to use a prompting module like
IO::Prompt
or
IO::Prompt::Tiny
in cases where it is not reasonable to do so because the goal is to obtain interactive input from the user. This makes things more compact.
But there are examples of this sort of thing that go in the other direction, too. In Python, the recommended way to create a standalone symbolic constant (i.e., one that is not part of an enumeration) is to just make it a regular variable but use all capital letters and avoid changing it. Static analyzers like pylint
can notice if it is modified. So, in Python, one might write:
In Perl the old way to make a symbolic constant was:
use constant NUMBER_OF_REPS => 10;
That still works fine, but it is looked down on for a few reasons. Mainly the issue is that the constant is accessed through the bareword NUMBER_OF_REPS
, which breaks the usual expectation to use a sigil, and also prevents it from being used in situations that require a sigil, like string interpolation. A newer method that is generally considered better is to install the Readonly
module (it's not necessarily installed by default) and use:
Then one can write things like:
Readonly my $NUMBER_OF_REPS => 10;
Even better is to install and use the Const::Fast
module.
Then one can write things like:
const my $NUMBER_OF_REPS => 10;
One thing about Perl that often saves space is that you do not have to convert strings to numbers before using them as numbers. For each of the dynamic types a scalar value can be, the Perl interpreter automatically keeps track of whether or not there was a value computed for it of that type and what it was, as well as whether or not it is current. So if you have strings that you then use as numbers, they shouldn't have to be converted to numbers again each time. Thus the Perl code
my @values = split ' ', <STDIN>;
works in place of the Python 3 code
but it also works in place of the Python 3 code:
values = list(map(int, input().split()))
However, when a value is used as the key for a hash (a hash in Perl is a hash table, like a dict in Python), it is used as a string. Thus, care becomes required when there might be multiple string representations for the same number. Even aside from the situation of extra leading zeros, this can happen with integers, because leading and trailing whitespace are allowed.
The Perl interpreter won't generate string representations of numbers with whitespace added (e.g., you don't have to worry about spaces being printed from print 3;
), but if it is not sanitized out of user input then it will still be there, and if the value is used a the key in a hash, then extra explicit processing is needed. Consider this Perl script, which reads numbers, one per line, stores them and their squares in a hash, then prints each "unique" pair in numerically increasing order:
#!/usr/bin/env perl
use strict;
use warnings;
my %squares;
while (<>) { # read one line at a time
chomp;
$squares{$_} = $_ * $_;
}
print "$_ => $squares{$_}\n" for (sort { $a <=> $b } keys %squares);
To deal with all the possible situations where this can happen, people sometimes use tricks like $squares{$_ + 0} = $_ * $_
, or writing $_ += 0
first.
The other thing that may stand out is, in Perl, one often writes things like:
my @sorted_numbers = sort { $a <=> $b } @numbers;
sorted_numbers = sorted(numbers)
Or if numbers
is a list and it's sufficient to just make it be sorted, then:
You can use this in Perl, but it does not do the right thing for numbers:
my @sorted_numbers = sort @numbers;
The reason is that, if you don't pass it a block of subroutine to tell it what kind of comparison to use, then it treats the values as strings and uses lexicographic string comparison on them, regardless of whether or not they were ever strings. (Hash keys are strings in Perl, but this happens even when the list being sorted was never originally strings.)
In Python, each object has a specific type, and it knows its type. This forces you to convert objects between types explicitly in most situations where it's not already the type you need. But it means that the objects, rather than the operators your choose to use on them, know what needs to be done, which means that operators like <
are numerical for numbers and lexicographic for strings. In Perl, scalars can act like any of several different dynamic types, but they use different operators.
This is not bad. It's a totally reasonable design choice, as is Python's. But it means you have to distinguish between operators like ==
and <
(and several others) that perform numerical comparison and eq
and le
(and several others) that perform string comparison. It's sort of like the comparison tests with the [
/test
command in shell scripting and Bash's [[
keyword--except that the meanings are reversed.
For the same reason, you can use +
in Python for both addition and string concatenation, and *
for both multiplication and string repetition. In Perl, +
and *
are always arithmetic, and you must use .
and x
for concatenation and repetition.