Από Python σε Perl#

Ξέρετε Python. Γράφετε list comprehensions στον ύπνο σας, φτάνετε σε ένα dict χωρίς να το σκέφτεστε, και το for x in xs: είναι μυϊκή μνήμη. Αυτός ο οδηγός αντιστοιχίζει καθεμία από αυτές τις συνήθειες στην Perl, ώστε να γίνετε παραγωγικοί στο pperl μέσα σε ένα απόγευμα αντί να ξαναμάθετε προγραμματισμό από το μηδέν.

Δύο σοκ φτάνουν μέσα στα πρώτα πέντε λεπτά, οπότε γνωρίστε τα τώρα:

  • Η Perl χρησιμοποιεί άγκιστρα και ερωτηματικά, όχι εσοχές. Δεν υπάρχει σημαντικός λευκός χαρακτήρας. Ένα μπλοκ είναι { ... }· μια εντολή τελειώνει με ;. Κάντε εσοχές για τη δική σας ψυχική υγεία, αλλά ο αναλυτής τις αγνοεί.

  • Οι μεταβλητές φορούν sigils. Ένα βαθμωτό είναι $x, ένας πίνακας είναι @x, ένα hash είναι %x. Το sigil είναι μέρος του πώς διαβάζετε τη μεταβλητή, όχι απλώς του ονόματός της. Αυτή είναι η μεγαλύτερη επιφανειακή διαφορά από την Python, και η πρώτη ενότητα δεν αφορά τίποτε άλλο.

Every example here runs on pperl, a Rust reimplementation of Perl 5.42. Each Perl snippet was executed on pperl; the # ... comments show its real output. Start your scripts with:

use v5.42;

Αυτή η μία γραμμή ενεργοποιεί το strict (καμία τυχαία καθολική μεταβλητή), τα warnings, την ενσωματωμένη say (ένα print με τελικό χαρακτήρα νέας γραμμής, όπως το print της Python), τις υπογραφές υπορουτινών, και το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται σε όλον αυτόν τον οδηγό.

Πώς να το διαβάσετε αυτό#

Μεταβλητές και sigils#

Στην Python ένα όνομα απλώς ονοματίζει μια τιμή. Στην Perl το sigil σας λέει το σχήμα: $ για μία τιμή, @ για μια διατεταγμένη λίστα, % για μια αντιστοίχιση κλειδιού/τιμής. Το my δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, το αντίστοιχο της Perl για μια συνηθισμένη τοπική σύνδεση.

name = "Ada"
langs = ["Python", "Perl"]
ages = {"Ada": 36, "Linus": 56}
use v5.42;
my $name = "Ada";
my @langs = ("Python", "Perl");
my %ages  = (Ada => 36, Linus => 56);
say $name;            # Ada
say $langs[0];        # Python
say $ages{Ada};       # 36

Η παγίδα: όταν φτάνετε μέσα σε ένα σύνολο για ένα στοιχείο, το sigil ακολουθεί την τιμή που παίρνετε πίσω, όχι τον περιέκτη. Ένα μεμονωμένο στοιχείο του @langs είναι βαθμωτό, οπότε είναι $langs[0], όχι @langs[0]· μία τιμή από το %ages είναι $ages{Ada}. Η μορφή @/% είναι όλη η συλλογή· η μορφή $ είναι ένα κομμάτι της.

Συμβολοσειρές#

Τα διπλά εισαγωγικά παρεμβάλλουν μεταβλητές, τα μονά όχι. Δεν υπάρχει f-string επειδή η προεπιλεγμένη συμβολοσειρά είναι ένα f-string. Η συνένωση είναι ., όχι + (το οποίο προορίζεται για αριθμούς).

name = "Ada"
greeting = f"Hello, {name}"
shout = "AB" * 3
use v5.42;
my $name = "Ada";
my $greeting = "Hello, $name";   # Hello, Ada
my $literal  = 'Hello, $name';   # Hello, $name  (no interpolation)
my $joined   = "AB" . "CD";      # ABCD
my $shout    = "AB" x 3;         # ABABAB

Η παγίδα: η επανάληψη συμβολοσειράς είναι x, όχι *, και ο τελεστής ένωσης είναι ., όχι +. Το μπέρδεμά τους είναι το πιο συχνό τυπογραφικό λάθος της πρώτης μέρας για έναν προγραμματιστή της Python. Τα στοιχεία πίνακα και οι τιμές hash παρεμβάλλονται και μέσα σε διπλά εισαγωγικά: το "First: $langs[0]" δουλεύει όπως είναι γραμμένο.

Αριθμοί#

Η Perl έχει έναν αριθμητικό τύπο στην επιφάνεια· δεν δηλώνετε int έναντι float. Η διαίρεση πάντα παράγει έναν αριθμό κινητής υποδιαστολής, ακριβώς όπως η Python 3.

print(7 / 2)        # 3.5
print(7 // 2)       # 3
print(2 ** 10)      # 1024
print(10 % 3)       # 1
use v5.42;
say 7 / 2;          # 3.5
say int(7 / 2);     # 3
say 2 ** 10;        # 1024
say 10 % 3;         # 1

Η παγίδα: οι τελεστές σύγκρισης και ισότητας έρχονται σε δύο οικογένειες, και επιλέγετε με βάση τον τύπο των τελεστέων, όχι των μεταβλητών. Οι αριθμοί χρησιμοποιούν ==, !=, <, >, <=>· οι συμβολοσειρές χρησιμοποιούν eq, ne, lt, gt, cmp. Το "10" == "10.0" είναι αληθές (αριθμητικά), αλλά το "10" eq "10.0" είναι ψευδές (συμβολοσειρά). Η χρήση == σε συμβολοσειρές, ή eq σε αριθμούς, είναι ένα συχνό και σιωπηλό σφάλμα.

Πίνακες και λίστες#

Ένας πίνακας της Perl είναι η list σας στην Python. Τα ρήματα έχουν μετονομαστεί αλλά οι λειτουργίες είναι ίδιες, και οι αρνητικοί δείκτες δουλεύουν όπως περιμένετε.

nums = [1, 2, 3, 4, 5]
nums.append(6)
last = nums.pop()
print(len(nums))    # 5
print(nums[1:3])    # [2, 3]
use v5.42;
my @nums = (1, 2, 3, 4, 5);
push @nums, 6;
my $last = pop @nums;       # 6
say scalar @nums;           # 5
say "@nums[1,2]";           # 2 3   (a slice)

Τα comprehensions γίνονται map και grep. Το map μετασχηματίζει (το [f(x) for x in xs] της Python)· το grep φιλτράρει (το [x for x in xs if p(x)] της Python). Το $_ είναι το τρέχον στοιχείο, η σιωπηρή μεταβλητή βρόχου της Perl.

squares = [x * x for x in nums]
evens   = [x for x in nums if x % 2 == 0]
desc    = sorted(nums, reverse=True)
use v5.42;
my @nums    = (1, 2, 3, 4, 5);
my @squares = map  { $_ * $_ }   @nums;   # 1 4 9 16 25
my @evens   = grep { $_ % 2 == 0 } @nums; # 2 4
my @desc    = sort { $b <=> $a }  @nums;  # 5 4 3 2 1

Η παγίδα: το sort συγκρίνει ως συμβολοσειρές από προεπιλογή, οπότε το sort (10, 9, 100) δίνει 10 100 9. Για αριθμούς πρέπει να περάσετε έναν συγκριτή: sort { $a <=> $b } @nums. Τα $a και $b είναι τα δύο στοιχεία που συγκρίνονται, και το <=> είναι ο αριθμητικός τρίτιμος τελεστής (το cmp είναι το αδελφάκι του για συμβολοσειρές).

Hashes#

Ένα hash της Perl είναι το dict σας στην Python. Δηλώνεται με %, και προσπελαύνεται μία τιμή τη φορά με $h{key}.

h = {"apple": 3, "pear": 5, "plum": 2}
print(h["apple"])               # 3
print(sorted(h.keys()))         # ['apple', 'pear', 'plum']
print("pear" in h)              # True
del h["plum"]
for k in sorted(h):
    print(k, "=>", h[k])
use v5.42;
my %h = (apple => 3, pear => 5, plum => 2);
say $h{apple};                          # 3
say join ",", sort keys %h;             # apple,pear,plum
say exists $h{pear} ? "yes" : "no";     # yes
delete $h{plum};
for my $k (sort keys %h) {
    say "$k => $h{$k}";
}

Η παγίδα: ο έλεγχος μέλους είναι exists $h{key}, όχι το in της Python, και το παχύ κόμμα => είναι ο ιδιωματικός διαχωριστής κλειδιού/τιμής. Το => επίσης βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του, οπότε το apple => 3 δεν χρειάζεται εισαγωγικά γύρω από το apple. Ένα hash δεν έχει σειρά· επαναλάβετε με sort keys %h όταν θέλετε μια σταθερή.

Περιβάλλον#

Αυτό είναι το κεφάλαιο που πρέπει να διαβάσετε δύο φορές. Η Python δεν έχει αντίστοιχο, και σχεδόν κάθε ιστορία «η Perl είναι παράξενη» ανάγεται σε αυτό.

Κάθε έκφραση στην Perl τρέχει είτε σε περιβάλλον λίστας είτε σε βαθμωτό περιβάλλον, που καθορίζεται από το πού εμφανίζεται, όχι από το τι είναι. Ο ίδιος πίνακας συμπεριφέρεται διαφορετικά σε καθένα. Η ανάθεση ενός πίνακα σε ένα βαθμωτό δίνει το μήκος του· η ανάθεσή του σε μια λίστα (ένα σύνολο μεταβλητών σε παρενθέσεις) αντιγράφει στοιχεία.

use v5.42;
my @list = (10, 20, 30);

my $count = @list;        # scalar context -> 3 (the length)
say $count;               # 3

my ($first) = @list;      # list context -> first element copied
say $first;               # 10

my ($a, $b) = @list;      # 10 and 20
say "$a $b";              # 10 20

Οι παρενθέσεις στα αριστερά είναι όλη η διαφορά. Το my $x = @list ρωτάει «πόσα;» και παίρνει 3. Το my ($x) = @list ρωτάει «δώσε μου τα στοιχεία» και παίρνει 10. Ένας προγραμματιστής της Python διαβάζει και τα δύο ως «ανάθεσε τη λίστα», που είναι ακριβώς η παγίδα.

Μια υπορουτίνα μπορεί να ρωτήσει σε ποιο περιβάλλον κλήθηκε, με wantarray, και να επιστρέψει διαφορετικά πράγματα ανάλογα:

use v5.42;
sub items {
    return wantarray ? (1, 2, 3) : "three items";
}
my @got = items();        # list context
my $msg = items();        # scalar context
say "@got";               # 1 2 3
say $msg;                 # three items

Οι ενσωματωμένες το χρησιμοποιούν κι αυτές. Το scalar @array επιβάλλει βαθμωτό περιβάλλον για να πάρει ένα πλήθος· μια αντιστοίχιση regex με /g επιστρέφει τη λίστα των αντιστοιχιών σε περιβάλλον λίστας και ένα αληθές/ψευδές σε βαθμωτό περιβάλλον. Το κλασικό ιδίωμα για το «μέτρα τις αντιστοιχίες» επιβάλλει μια ανάθεση λίστας σε βαθμωτό περιβάλλον με τη λεγόμενη μορφή countof:

use v5.42;
my $n = () = "a1b2c3" =~ /\d/g;
say $n;                   # 3

Η παγίδα: δεν υπάρχει μία μόνο απάντηση στο «τι κάνει αυτός ο πίνακας» - εξαρτάται από την περιβάλλουσα έκφραση. Όταν κάτι επιστρέφει έναν αριθμό που περιμένατε να είναι λίστα (ή το αντίστροφο), το περιβάλλον είναι ο πρώτος ύποπτος. Φτάστε στο scalar για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων.

Αλήθεια τιμών#

Οι ψευδείς τιμές της Perl είναι μια σύντομη, συγκεκριμένη λίστα: ο αριθμός 0, η κενή συμβολοσειρά "", η συμβολοσειρά "0", και το undef (το None της Perl). Όλα τα άλλα είναι αληθή - και αυτό περιλαμβάνει κάποιες συμβολοσειρές που ένας προγραμματιστής της Python υποθέτει ότι είναι ψευδείς.

use v5.42;
for my $v (0, 1, "", "0", "00", "0.0", "false") {
    say "'$v' is ", ($v ? "true" : "false");
}

Αυτό τυπώνει, ακριβώς:

'0' is false
'1' is true
'' is false
'0' is false
'00' is true
'0.0' is true
'false' is true

Η παγίδα: τα "00" και "0.0" είναι αληθή στην Perl, παρόλο που μοιάζουν αριθμητικά μηδέν, επειδή μόνο η ακριβής συμβολοσειρά "0" είναι ψευδής. Η συμβολοσειρά "false" είναι αληθής (είναι μια μη κενή, μη-"0" συμβολοσειρά). Και σε αντίθεση με την Python, ένας κενός πίνακας ή κενό hash δεν είναι ο ίδιος μια ψευδής τιμή - σε λογικό περιβάλλον ένας πίνακας δίνει το μήκος του, οπότε το if (@list) είναι αληθές όταν η λίστα είναι μη κενή, που είναι ο έλεγχος που θέλετε. Για μια επιλογή «χρησιμοποίησε αυτό αν είναι ορισμένο, αλλιώς μια προεπιλογή», χρησιμοποιήστε το defined-or //, που πέφτει πίσω μόνο στο undef (όχι στο 0 ή στο ""):

use v5.42;
my %cfg = (host => "localhost");
my $port = $cfg{port} // 8080;   # key absent -> undef -> 8080
say $port;                       # 8080

Ροή ελέγχου#

Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει. Το elif είναι elsif, και δεν υπάρχει : ούτε εσοχή - τα μπλοκ είναι σε άγκιστρα.

if n < 0:
    sign = "neg"
elif n == 0:
    sign = "zero"
else:
    sign = "pos"

for x in nums:
    print(x)

while running:
    step()
use v5.42;
my $sign;
if    ($n < 0)  { $sign = "neg"  }
elsif ($n == 0) { $sign = "zero" }
else            { $sign = "pos"  }

for my $x (@nums) { say $x }

while ($running) { step() }

Η Perl προσθέτει δύο πράγματα που δεν έχει η Python. Οι μεταθεματικοί τροποποιητές επιτρέπουν σε μια απλή εντολή να φέρει τη δική της συνθήκη ή βρόχο, κάτι που διαβάζεται φυσικά για μονόγραμμα:

use v5.42;
say $_ for @nums;                 # loop, one statement
say "negative" if $n < 0;         # guarded statement
$sum += $_ for @nums;             # accumulate

Και τα unless/until είναι οι αρνημένες μορφές των if/while, για όταν η θετική διατύπωση διαβάζεται αδέξια: say "ok" unless $error.

Η παγίδα: ο τριαδικός τελεστής αλυσιδώνεται όπως της Python, αλλά με ?/:, όχι if/else: my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos". Το ?: σε στυλ C είναι το ιδίωμα· δεν υπάρχει μορφή x if c else y.

Υπορουτίνες#

Το def γίνεται sub. Η σύγχρονη Perl έχει υπογραφές που μοιάζουν πολύ με τις λίστες παραμέτρων της Python, συμπεριλαμβανομένων των προεπιλογών.

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

def total(*nums):
    return sum(nums)
use v5.42;
sub greet ($name, $greeting = "Hello") {
    return "$greeting, $name!";
}
say greet("Ada");           # Hello, Ada!
say greet("Linus", "Hi");   # Hi, Linus!

sub total (@nums) {
    my $sum = 0;
    $sum += $_ for @nums;
    return $sum;
}
say total(1, 2, 3, 4);      # 10

Τα ονοματισμένα ορίσματα αντιστοιχίζονται σε μια παράμετρο hash, αφού τα ζεύγη key => value ισοπεδώνονται σε μια λίστα που μια υπογραφή %opts συγκεντρώνει:

use v5.42;
sub connect_to (%opts) {
    return "host=$opts{host} port=$opts{port}";
}
say connect_to(host => "localhost", port => 8080);
# host=localhost port=8080

Η παγίδα: μια sub επιστρέφει την τιμή της τελευταίας της έκφρασης ακόμη και χωρίς return, και η επιστροφή είναι ευαίσθητη στο περιβάλλον (δείτε Περιβάλλον). Αν κάνετε return @list, ο καλών παίρνει τη λίστα σε περιβάλλον λίστας αλλά το πλήθος σε βαθμωτό περιβάλλον. Να είστε εσκεμμένοι σχετικά με το τι επιστρέφετε όταν μπορεί να χρησιμοποιηθεί με οποιονδήποτε τρόπο.

Αναφορές#

Οι πίνακες και τα hashes της Perl δεν εμφωλεύονται απευθείας· τα εμφωλεύετε μέσω αναφορών, που είναι το αντίστοιχο της Perl για τις λαβές αντικειμένων της Python (στην Python κάθε περιέκτης είναι ήδη αναφορά, οπότε αυτό το βήμα είναι εκεί αόρατο σε εσάς). Μια αναφορά είναι ένα βαθμωτό που δείχνει σε ένα σύνολο. Πάρτε μία με \, ακολουθήστε τη με ->.

data = {"name": "Ada", "langs": ["Perl", "Python"]}
print(data["langs"][1])     # Python
data["langs"].append("Ruby")
use v5.42;
my $data = { name => "Ada", langs => ["Perl", "Python"] };
say $data->{name};                  # Ada
say $data->{langs}[1];              # Python
push $data->{langs}->@*, "Ruby";    # postfix deref to push
say "@{$data->{langs}}";           # Perl Python Ruby

Το { ... } φτιάχνει μια ανώνυμη αναφορά hash και το [ ... ] μια ανώνυμη αναφορά πίνακα - τα κυριολεκτικά δομικά στοιχεία των εμφωλευμένων δεδομένων, που αντιστοιχούν στις κυριολεξίες {} και [] της Python. Το βέλος -> ακολουθεί μια αναφορά κατά ένα βήμα· ανάμεσα σε δύο δείκτες είναι προαιρετικό, οπότε τα $data->{langs}[1] και $data->{langs}->[1] είναι ίδια.

Οι ανώνυμες subs είναι closures, ακριβώς όπως το lambda της Python αλλά χωρίς τον περιορισμό της μίας έκφρασης:

use v5.42;
my $double = sub ($n) { $n * 2 };
say $double->(21);          # 42

Η παγίδα: οι μεταθεματικές μορφές αποαναφοράς (->@*, ->%*, ->$*) είναι ο σύγχρονος, ευανάγνωστος τρόπος να αποαναφέρετε ένα ολόκληρο σύνολο, και το "@{ ... }" είναι ο τρόπος να παρεμβάλετε έναν αποαναφερμένο πίνακα μέσα σε μια συμβολοσειρά. Οι παλαιότερες μορφές @$ref και ${$ref}{key} εξακολουθούν να δουλεύουν και εμφανίζονται σε υπάρχοντα κώδικα, αλλά προτιμήστε το βέλος και τις μεταθεματικές μορφές σε νέο κώδικα.

Κανονικές εκφράσεις#

Στην Python το regex είναι η βιβλιοθήκη re. Στην Perl είναι σύνταξη: το =~ συνδέει ένα μοτίβο με μια συμβολοσειρά, το m// αντιστοιχίζει, το s/// αντικαθιστά. Οι ομάδες σύλληψης καταλήγουν στα $1, $2, και οι ονομαστικές συλλήψεις στο %+.

import re
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", s)
if m:
    year, month, day = m.groups()
t = re.sub(",", ";", "a,b,c")
parts = "one,two,three".split(",")
use v5.42;
my $s = "2026-06-20";
if ($s =~ /(\d{4})-(\d{2})-(\d{2})/) {
    say "$1 $2 $3";                 # 2026 06 20
}
if ($s =~ /(?<year>\d{4})/) {
    say $+{year};                   # 2026
}
(my $t = "a,b,c") =~ s/,/;/g;       # a;b;c
my @parts = split /,/, "one,two,three";   # one two three

Μια αντιστοίχιση /g σε περιβάλλον λίστας επιστρέφει όλες τις συλλήψεις, που είναι το άμεσο αντίστοιχο του re.findall:

use v5.42;
my @nums = "x1y2z3" =~ /(\d)/g;     # 1 2 3
say "@nums";

Η παγίδα: το s/// τροποποιεί τον στόχο του επιτόπια και επιστρέφει τον αριθμό των αντικαταστάσεων, όχι μια νέα συμβολοσειρά. Γι” αυτό το παράδειγμα αντιγράφει πρώτα με (my $t = "a,b,c") και μετά αντικαθιστά στο αντίγραφο - αλλιώς θα επεξεργαζόσασταν μια κυριολεξία συμβολοσειράς. Για να πάρετε μια νέα συμβολοσειρά χωρίς να αγγίξετε την αρχική, χρησιμοποιήστε τη σημαία /r: my $t = $s =~ s/,/;/gr.

I/O αρχείων και ροών#

Το ιδίωμα είναι το open με τρία ορίσματα και ένα λεξιλογικό filehandle, το άμεσο αντίστοιχο του with open(...) as f: της Python. Ο τελεστής με γωνιώδεις αγκύλες <$fh> διαβάζει μια γραμμή, με τον ίδιο ρόλο όπως η επανάληψη ενός αντικειμένου αρχείου της Python.

with open("/tmp/sample.txt", "w") as f:
    for i in range(1, 4):
        f.write(f"line {i}\n")

with open("/tmp/sample.txt") as f:
    for line in f:
        print("got:", line.rstrip())
use v5.42;
my $path = "/tmp/sample.txt";

open my $wh, '>', $path or die "cannot write: $!";
print {$wh} "line $_\n" for 1 .. 3;
close $wh;

open my $rh, '<', $path or die "cannot read: $!";
while (my $line = <$rh>) {
    chomp $line;
    say "got: $line";       # got: line 1 ... got: line 3
}
close $rh;

Για να ρουφήξετε (slurp) όλες τις γραμμές μονομιάς, διαβάστε το handle σε περιβάλλον λίστας:

use v5.42;
open my $fh, '<', "/tmp/sample.txt" or die $!;
my @lines = <$fh>;          # all lines, including newlines
say scalar @lines;          # 3

Η παγίδα: μια γραμμή που διαβάστηκε με <$fh> κρατάει τον τελικό χαρακτήρα νέας γραμμής· καλέστε chomp για να τον αφαιρέσετε (το αντανακλαστικό line.rstrip() της Python). Και το open επιστρέφει ψευδές σε αποτυχία αντί να εγείρει εξαίρεση, οπότε το ιδίωμα or die "...: $!" είναι υποχρεωτικό - το $! είναι το μήνυμα σφάλματος συστήματος, το errno της Perl.

Αρθρώματα και πακέτα#

Το import γίνεται use. Ο διαχωριστής χώρου ονομάτων είναι το ::, όχι το ., και ένα άρθρωμα Foo::Bar βρίσκεται στο Foo/Bar.pm στη διαδρομή ένταξης @INC (το sys.path της Perl). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων, όπως ακριβώς το from module import name.

from math import sqrt
import json
data = json.loads(text)
use v5.42;
use List::Util qw(sum0 max first);
say sum0(1 .. 10);                       # 55
say max(3, 7, 2);                        # 7
say first { $_ % 2 == 0 } (1, 3, 4, 5);  # 4

Η τυπική βιβλιοθήκη είναι μεγάλη και πολλά από αυτά για τα οποία θα κατέφευγες στο PyPI έρχονται στον πυρήνα ή στο CPAN, το αρχείο πακέτων της Perl. Τα List::Util, Scalar::Util, POSIX, File::Spec, και Data::Dumper είναι τα καθημερινά.

Η παγίδα: το use τρέχει κατά τη μεταγλώττιση, όχι στο σημείο του αρχείου όπου εμφανίζεται, οπότε η σειρά των γραμμών use σε σχέση με τον κώδικά σας δεν έχει σημασία όπως έχει η τοποθέτηση του import. Η εισαγωγή είναι επίσης προαιρετική ανά όνομα: το use List::Util qw(sum0 max) φέρνει ακριβώς τα sum0 και max στον χώρο ονομάτων σας, τίποτε άλλο.

Αντικείμενα#

Για νέο κώδικα, η Perl 5.42 έχει μια σύνταξη class πρώτης τάξης που θα φανεί οικεία ερχόμενοι από την Python. Τα πεδία δηλώνονται με field, οι μέθοδοι με method, και το $self είναι διαθέσιμο μέσα σε κάθε μέθοδο ακριβώς όπως το self στην Python (δεν το αναφέρετε ως παράμετρο).

class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    def to_string(self):
        return f"({self.x}, {self.y})"
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
        return self

p = Point(x=3, y=4)
print(p.to_string())        # (3, 4)
p.move(1, 1)
print(p.to_string())        # (4, 5)
use v5.42;
use feature 'class';

class Point {
    field $x :param = 0;
    field $y :param = 0;
    method to_string { return "($x, $y)" }
    method move ($dx, $dy) {
        $x += $dx;
        $y += $dy;
        return $self;
    }
}

my $p = Point->new(x => 3, y => 4);
say $p->to_string;          # (3, 4)
$p->move(1, 1);
say $p->to_string;          # (4, 5)

Ο κατασκευαστής δημιουργείται για εσάς: το :param σημειώνει ένα πεδίο που η αυτόματα κατασκευασμένη new δέχεται ως ονοματισμένο όρισμα, και το = 0 είναι η προεπιλογή του. Η κληρονομικότητα είναι :isa:

use v5.42;
use feature 'class';

class Point3D :isa(Point) {
    field $z :param = 0;
    method z { return $z }
}

my $q = Point3D->new(x => 1, y => 2, z => 3);
say $q->to_string, " z=", $q->z;   # (1, 2) z=3

Η παγίδα: το χαρακτηριστικό class εξακολουθεί να σημειώνεται ως πειραματικό στην Perl 5.42, και στο pperl οι ειδοποιήσεις «class is experimental» τυπώνονται στο stderr ακόμη και με no warnings 'experimental::class' εντός εμβέλειας. Δεν επηρεάζουν την έξοδο του προγράμματός σας, οπότε ένα σενάριο που τυπώνει στο stdout συμπεριφέρεται ακριβώς όπως φαίνεται παραπάνω· απλώς αναμένετε τις ειδοποιήσεις στη ροή σφαλμάτων. Για το παλαιότερο μοντέλο αντικειμένων βασισμένο στο bless που θα συναντήσετε σε υπάρχοντα κώδικα, και για ρόλους, λεπτομέρειες κληρονομικότητας, και μετάβαση, δείτε τον οδηγό Αντικειμενοστραφούς Προγραμματισμού.

Χειρισμός σφαλμάτων#

Το try/except της Python έχει δύο αντίστοιχα. Το κλασικό τυλίγει κώδικα σε eval { } και εξετάζει το $@ (το σφάλμα που πιάστηκε) μετά:

try:
    risky(-1)
except Exception as e:
    print("caught:", e)
use v5.42;
my $result = eval { risky(-1) };
if ($@) {
    print "caught: $@";     # caught: negative!
}

Η σύγχρονη μορφή είναι το try/catch, που διαβάζεται σχεδόν ακριβώς όπως της Python:

use v5.42;
use feature 'try';

try {
    risky(-4);
}
catch ($e) {
    print "caught: $e";     # caught: negative!
}

όπου η πλευρά που εγείρει είναι το die:

use v5.42;
sub risky ($n) {
    die "negative!\n" if $n < 0;
    return sqrt $n;
}

Η παγίδα: το die με μια συμβολοσειρά που τελειώνει σε \n παράγει ακριβώς αυτό το μήνυμα· το die με μια συμβολοσειρά που δεν τελειώνει σε χαρακτήρα νέας γραμμής παίρνει αυτόματα την προσθήκη " at SCRIPT line N.\n". Τελειώστε τις συμβολοσειρές σφάλματός σας με \n όταν θέλετε ένα καθαρό μήνυμα και παραλείψτε το όταν θέλετε το ίχνος αρχείου-και-γραμμής. Σημειώστε επίσης τις δύο μεταβλητές σφάλματος: το $@ είναι η τελευταία εξαίρεση που πιάστηκε (το except ... as e της Python), ενώ το $! είναι το τελευταίο σφάλμα συστήματος (ένα αποτυχημένο open, η συμβολοσειρά errno).

Ιδιωματισμοί#

Μερικά μοτίβα που θα βλέπετε καθημερινά και θα θέλετε στα δάχτυλά σας.

Ταξινομήστε μια λίστα με βάση ένα υπολογισμένο κλειδί (ο Schwartzian transform - το sorted(xs, key=f) της Python γινόμενο με το χέρι, διακόσμησε μετά ταξινόμησε μετά αφαίρεσε τη διακόσμηση):

use v5.42;
my @words = qw(apple fig pear);
my @by_length =
    map  { $_->[1] }
    sort { $a->[0] <=> $b->[0] }
    map  { [ length $_, $_ ] } @words;
say "@by_length";           # fig pear apple

Φτιάξτε ένα hash από μια λίστα, το αντίστοιχο του dict-comprehension ({w: len(w) for w in words}):

use v5.42;
my @words = qw(apple pear fig);
my %len = map { $_ => length $_ } @words;
say "$_ => $len{$_}" for sort keys %len;
# apple => 5
# fig => 3
# pear => 4

Και το pperl είναι ένα εργαλείο γραμμής εντολών στην εξειδικευμένη θέση των awk/sed. Το μονόγραμμο με σιωπηρό βρόχο είναι το καθημερινό αντανακλαστικό του προγραμματιστή της Perl:

pperl -lane '$s += $F[-1]; END { print $s }' data.txt

Η παγίδα: διαβάστε τον οδηγό Μονόγραμμων για τους διακόπτες (-n, -p, -a, -l, -e) και τις δεκάδες συνταγές που είναι χτισμένες πάνω τους· είναι ένα από τα πράγματα με τη μεγαλύτερη μόχλευση που μπορείτε να μάθετε ερχόμενοι από υπόβαθρο κελύφους-και-Python.

Παγίδες ερχόμενοι από την Python#

Οι παγίδες, συγκεντρωμένες για μια τελική σάρωση πριν αρχίσετε να γράφετε:

  • Τα sigils αλλάζουν με τη χρήση. Ένα στοιχείο του @a είναι $a[0]· μία τιμή του %h είναι $h{k}. Το sigil του περιέκτη και το sigil του στοιχείου διαφέρουν.

  • Το . ενώνει συμβολοσειρές, το + προσθέτει αριθμούς. Ποτέ μην κάνετε + δύο συμβολοσειρές για να τις συνενώσετε· αυτό τις εξαναγκάζει σε αριθμούς και προειδοποιεί.

  • Δύο οικογένειες σύγκρισης. ==/</<=> για αριθμούς, eq/lt/ cmp για συμβολοσειρές. Επιλέξτε με βάση τον τύπο του τελεστέου, όχι με βάση τη μεταβλητή.

  • Το sort ταξινομεί ως συμβολοσειρές από προεπιλογή. Περάστε { $a <=> $b } για αριθμητική σειρά.

  • Περιβάλλον. Το my $x = @a είναι το μήκος· το my ($x) = @a είναι το πρώτο στοιχείο. Οι παρενθέσεις είναι όλη η διαφορά. Ξαναδιαβάστε το Περιβάλλον όταν μια τιμή επιστρέφει με λάθος σχήμα.

  • Εκπλήξεις αλήθειας. Το "0" είναι ψευδές, αλλά τα "00" και "0.0" είναι αληθή. Μόνο τα 0, "", "0", και undef είναι ψευδή.

  • Το s/// επεξεργάζεται επιτόπια και επιστρέφει ένα πλήθος· χρησιμοποιήστε τη σημαία /r για να πάρετε μια νέα συμβολοσειρά αντ” αυτού.

  • Το open δεν εγείρει εξαίρεση. Χρησιμοποιήστε open ... or die "...: $!".

  • Κάντε chomp στις αναγνώσεις γραμμών σας. Το <$fh> κρατάει τον τελικό χαρακτήρα νέας γραμμής.

  • Η μη-ASCII πηγή χρειάζεται use utf8;. Υπό use v5.42; ένα κυριολεκτικό μη-ASCII byte στην πηγή σας είναι σφάλμα μεταγλώττισης χωρίς αυτό. Κρατήστε τα δεδομένα συμβολοσειρών ASCII εκτός αν προσθέσετε ρητά use utf8;.

  • The class feature prints experimental notices to stderr on pperl. Correct output, noisy error stream.