Παρεμβολή#

Οι συμβολοσειρές σε διπλά εισαγωγικά, τα here-docs, τα μοτίβα regex, η αντικατάσταση s/.../.../ και η qq// κάνουν όλα παρεμβολή: αναπτύσσουν μεταβλητές και ορισμένες ακολουθίες διαφυγής επιτόπου. Οι συμβολοσειρές σε μονά εισαγωγικά, η q// και το αριστερό μοτίβο της tr/.../.../ δεν το κάνουν. Η γνώση ποιες κατασκευές κάνουν παρεμβολή και ακριβώς ποιες μορφές αναπτύσσονται είναι μία από τις πιο χρήσιμες γνώσεις Perl.

my $name = "Alice";
my @nums = (1, 2, 3);

"hello, $name"              # "hello, Alice"
'hello, $name'              # 'hello, $name' - single quotes don't interpolate
"@nums in a string"         # "1 2 3 in a string"
"\$name is $name"           # "$name is Alice"

Τι παρεμβάλλεται#

Μέσα σε "" (και τα άλλα περιβάλλοντα τύπου διπλών εισαγωγικών), η Perl αναπτύσσει ακριβώς δύο μορφές μεταβλητής:

$name              # scalar - the simple form
@list              # array  - joined by $" (one space by default)

Επιπλέον οι μορφές subscript με αγκύλες και άγκιστρα που συνθέτονται πάνω σε αυτές:

$arr[0]            # one element of @arr
$h{key}            # one value from %h
$ref->[0]          # via array ref
$ref->{key}        # via hash ref
@arr[1, 2]         # array slice
@h{qw(a b)}        # hash slice

Σημειώστε ότι ολόκληρα hashes δεν παρεμβάλλονται:

"hash is %h"       # "hash is %h" - literal '%' is fine, '%h' is NOT magical

Αν θέλετε ένα hash εκτυπωμένο σε συμβολοσειρά, μορφοποιήστε το ρητά με Data::Dumper ή χειροκίνητο join.

Τι δεν παρεμβάλλεται#

&sub               # function calls don't interpolate
sub_with_args($x)  # neither do calls with arguments
$h->method         # method calls don't interpolate
$obj->{a} + 1      # arithmetic doesn't interpolate
$arr[0+1]          # this DOES interpolate as $arr[0+1] - subscripts allow expressions

"\u\L$name"        # case-modifying escapes - these DO interpolate
"\x{2603}"         # numeric escapes - DO interpolate (a snowman)

Ο πρακτικός κανόνας: οι μεταβλητές και τα subscripts τους παρεμβάλλονται· οι κλήσεις συναρτήσεων/μεθόδων και οι αριθμητικές πράξεις πάνω σε αυτές δεν παρεμβάλλονται. Όταν θέλετε παρεμβολή μιας έκφρασης, δείτε τις κατασκευές @{[ ... ]} και ${\ ... } παρακάτω.

Οι αγκύλες ${name}#

Οι αγκύλες γύρω από ένα όνομα μεταβλητής αποσαφηνίζουν πού τελειώνει το όνομα:

my $who = "Larry";

"${who}speak"          # "Larryspeak"
"$whospeak"             # "" with warning: $whospeak is uninitialised
"${who}::sub"           # "Larry::sub"
"$who::sub"             # the variable $who::sub from package who - bug

Χωρίς τις αγκύλες, η Perl είναι άπληστη: η $whospeak διαβάζεται ως ένα αναγνωριστικό, η $who::sub διαβάζεται ως πλήρως κατονομασμένο όνομα. Οι αγκύλες αναγκάζουν τον αναλυτή να σταματήσει στο }.

Αυτή δεν είναι η ίδια μορφή αγκυλών με ένα subscript hash:

"$h{key}"              # hash element                 - what most people mean
"${h}{key}"            # hash element in a different parse path - same result
"${h{key}}"            # also the same
"${\(\$h{key})}"        # the absurd "interpolate an expression" form

Οι αγκύλες ${name} χρησιμοποιούνται κυρίως στην περίπτωση αποσαφήνισης (όνομα ακολουθούμενο από αλφαριθμητικούς χαρακτήρες που αλλιώς θα καταναλώνονταν), όχι για πρόσβαση σε hash.

Οι μορφές αποαναφοράς με αγκύλες @{...} και ${...}#

Όταν θέλετε να αποαναφέρετε κάτι μέσα σε συμβολοσειρά, η μορφή με αγκύλες επιτρέπει η έκφραση αποαναφοράς να είναι αυθαίρετη:

my $config = { users => ['Alice', 'Bob'] };

"users: @{ $config->{users} }"     # "users: Alice Bob"
"users: $config->{users}"          # "users: ARRAY(0x...)" - wrong; whole arrayref stringified
"users: @$config->{users}"         # parse error / wrong

The @{ EXPR } form deref’s EXPR as an array. The braces are required because the deref target is an expression, not a plain variable. @$config->{users} doesn’t parse the way intended

  • @$config would deref $config as an array, and ->{users} is a hash-element access on what’s left.

Το ίδιο ισχύει για ${ EXPR } (βαθμωτή αποαναφορά) και %{ EXPR } (αποαναφορά hash, που επιστρέφει επίπεδη λίστα, σπάνια χρήσιμη στην παρεμβολή).

@{[ expr ]} - παρεμβολή οποιασδήποτε έκφρασης λίστας#

Για παρεμβολή του αποτελέσματος μιας αυθαίρετης έκφρασης, τυλίξτε την σε @{[ ... ]}:

my $name = "World";
print "hello, @{[ uc $name ]}!\n";              # "hello, WORLD!"

print "found @{[ scalar @hits ]} matches\n";     # interpolate a count

print "users: @{[ join ', ', @users ]}\n";       # interpolate a join

Ο μηχανισμός: η [ ... ] κατασκευάζει μια ανώνυμη αναφορά πίνακα με περιεχόμενα την τιμή της έκφρασης σε περιβάλλον λίστας· η @{ ... } στη συνέχεια αποαναφέρει αυτή την αναφορά πίνακα σε λίστα, η οποία παρεμβάλλεται όπως μια λίστα (ενωμένη με $").

Αυτός είναι ο κανονικός τρόπος αντιμετώπισης του «θέλω κλήση συνάρτησης μέσα σε συμβολοσειρά με διπλά εισαγωγικά». Η εναλλακτική είναι η συνένωση:

print "hello, " . uc($name) . "!\n";             # equivalent

Η μορφή @{[ ... ]} είναι πιο πυκνή όταν υπάρχουν πολλαπλές παρεμβολές· η συνένωση είναι πιο σαφής όταν υπάρχει μόνο μία. Σε πρότυπα με πολλά πεδία, η printf ή η sprintf είναι συνήθως ακόμα πιο σαφής:

printf "found %d matches\n", scalar @hits;

Χρησιμοποιήστε @{[ ... ]} όταν θέλετε πραγματικά μια έκφραση Perl στη μέση μιας συμβολοσειράς και οι εναλλακτικές είναι πιο άσχημες.

${\ expr } - παρεμβολή βαθμωτής έκφρασης#

Το βαθμωτό αντίστοιχο της @{[ ... ]}:

print "answer: ${\ 6 * 7 }\n";                   # "answer: 42"
print "ts: ${\ scalar localtime }\n";             # "ts: Mon Apr 27 ..."

Η \ EXPR δημιουργεί αναφορά στη βαθμωτή τιμή της EXPR· η ${ ... } την αποαναφέρει. Το αποτέλεσμα είναι μία τιμή, παρεμβαλλόμενη ως βαθμωτό.

Στις περισσότερες περιπτώσεις η μορφή @{[ ... ]} είναι πιο ομοιόμορφη - λειτουργεί τόσο σε βαθμωτό όσο και σε περιβάλλον λίστας. Η ${\ ... } είναι περιστασιακά πιο σαφής όταν θέλετε συγκεκριμένα βαθμωτό περιβάλλον:

"got @{[ items() ]} items"      # items() called in LIST context
"got ${\ items() } items"        # items() called in SCALAR context

Αμφισημία subscript hash σε μοτίβα#

Μέσα σε μοτίβο regex (που είναι τύπου διπλών εισαγωγικών), ο αναλυτής πρέπει να μαντέψει αν η $foo[bar] σημαίνει «παρεμβολή $foo[bar] (ένα στοιχείο της @foo)» ή «παρεμβολή $foo και στη συνέχεια αντιστοίχιση κλάσης χαρακτήρων [bar]»:

my @foo = ('hi');
$x =~ /$foo[abc]/                 # @foo exists - parsed as $foo[abc] (one element)
$x =~ /$foo[abc]/                 # @foo doesn't exist - parsed as $foo + char class

Η αποσαφήνιση γίνεται με τη μορφή αγκυλών:

$x =~ /${foo}[abc]/               # $foo + character class [abc]
$x =~ /$foo[5]/                   # element 5 of @foo
$x =~ /\Q$foo\E[abc]/             # $foo (literal) + character class

Ο \Q...\E απενεργοποιεί την ερμηνεία μετα-χαρακτήρων εντός της εμβέλειάς του, κάτι που είναι η πιο καθαρή λύση όταν η μεταβλητή είναι είσοδος χρήστη.

Heredocs#

Τα heredocs είναι τύπου διπλών εισαγωγικών εκτός αν ο δείκτης είναι σε μονά εισαγωγικά:

my $msg = <<EOM;
Hello, $name!
The hash is %h.
EOM
# "Hello, Alice!\nThe hash is %h.\n" - $name interpolated, %h literal

my $msg = <<'EOM';
Hello, $name!
EOM
# "Hello, $name!\n" - single-quoted marker disables interpolation

Τα εσοχοποιημένα heredocs (Perl 5.26+) αγνοούν τα αρχικά κενά μέχρι τη μικρότερη ακολουθία σε οποιαδήποτε γραμμή:

my $msg = <<~EOM;
    Hello, $name!
    EOM
# "Hello, Alice!\n" - leading "    " stripped from every line

Η μορφή ~ προτιμάται σε σύγχρονο κώδικα· η μη εσοχοποιημένη μορφή είναι άσχημη μέσα σε βρόχους και σώματα μεθόδων.

Πραγματικό παράδειγμα: κατασκευή HTML με ασφάλεια#

my @items = ('Apples', 'Bananas', 'Cherries');

my $html = <<~HTML;
    <ul>
    @{[ map { "  <li>$_</li>" } @items ]}
    </ul>
    HTML

Η map επιστρέφει μια λίστα συμβολοσειρών <li>...</li>· η @{[ ... ]} τις παρεμβάλλει· η σιωπηρή $" είναι ένα κενό, δίνοντας:

<ul>
  <li>Apples</li>   <li>Bananas</li>   <li>Cherries</li>
</ul>

Για αλλαγές γραμμής μεταξύ στοιχείων, ορίστε τοπικά τη $":

my $html = do {
    local $" = "\n";
    <<~HTML;
        <ul>
        @{[ map { "  <li>$_</li>" } @items ]}
        </ul>
        HTML
};

Ή ενώστε ρητά:

my $li = join "\n", map { "  <li>$_</li>" } @items;
my $html = "<ul>\n$li\n</ul>\n";

Η ρητή join είναι συνήθως πιο σαφής για μη τετριμμένα πρότυπα. Η μορφή @{[ ... ]} κερδίζει τη θέση της όταν το πρότυπο είναι κυρίως κυριολεκτικό κείμενο με έναν ή δύο ενσωματωμένους υπολογισμούς.

Τι ελέγχει η ειδική μεταβλητή $"#

Ο διαχωριστής λίστας στην παρεμβολή:

my @x = (1, 2, 3);
print "x = @x\n";                         # "x = 1 2 3" - default $" is " "

local $" = ", ";
print "x = @x\n";                         # "x = 1, 2, 3"

local $" = "\n  ";
print "items:\n  @x\n";                   # "items:\n  1\n  2\n  3"

Η $" είναι ο κανονικός τρόπος ελέγχου του πώς παρεμβάλλονται οι πίνακες χωρίς αναγραφή της συμβολοσειράς. Δείτε filehandle and printing state για την οικογένεια μεταβλητών διαχωριστή.

Δείτε επίσης#

  • Scalars, Arrays, Hashes - οι μορφές που αναπτύσσει η παρεμβολή.

  • References - η μορφή αποαναφοράς @{ EXPR } και τι κάνει η \ στην ${\ ... }.

  • $" - ο διαχωριστής λίστας για παρεμβολή πίνακα.

  • $,, $\, $/ - οι σχετικές μεταβλητές διαχωριστή για print και είσοδο.

  • sprintf, printf

    • the explicit format-string alternative when the template is mostly punctuation.

  • qq, q, qw - οι τελεστές τύπου εισαγωγικών με προσαρμοσμένους οριοθέτες.