Από PHP σε Perl#
Ξέρετε PHP. Φτάνετε στο $arr['key'] χωρίς να το σκέφτεστε, το foreach ($items as $item) είναι μυϊκή μνήμη, και ενώνετε συμβολοσειρές με . επειδή φυσικά το κάνετε. Η Perl και η PHP είναι στενά ξαδέλφια: και οι δύο είναι γλώσσες που χρησιμοποιούν sigils, πρακτικές, φτιαγμένες για να βγαίνει η δουλειά, και η PHP δανείστηκε πολλά από την Perl στις πρώτες της μέρες. Αυτός ο οδηγός αντιστοιχίζει τις συνήθειές σας στην PHP με την Perl ώστε να γίνετε παραγωγικοί στο pperl μέσα σε ένα απόγευμα.
Δύο σοκ φτάνουν μέσα στα πρώτα πέντε λεπτά, οπότε γνωρίστε τα τώρα:
Το sigil
$δεν είναι όλη η ιστορία. Στην PHP κάθε μεταβλητή είναι$x, πάντα. Στην Perl το sigil αλλάζει ανάλογα με το πώς προσπελαύνετε την τιμή: το@aείναι όλος ο πίνακας αλλά το$a[0]είναι ένα στοιχείο· το%hείναι όλο το hash αλλά το$h{k}είναι μία τιμή. Το δολάριο που ήδη ξέρετε σημειώνει ένα μεμονωμένο βαθμωτό· τα@και%είναι νέα και η πρώτη ενότητα δεν αφορά τίποτε άλλο.Ο ένας τύπος
arrayτης PHP γίνεται δύο τύποι στην Perl. Έναarrayτης PHP είναι μια διατεταγμένη αντιστοίχιση που κάνει χρέη και λίστας και λεξικού. Η Perl το χωρίζει σε@array(με ακέραιους δείκτες) και%hash(με κλειδιά συμβολοσειρών), και επιλέγετε εξαρχής ποιο έχετε. Αυτός ο διαχωρισμός καθοδηγεί τις ενότητες Πίνακες και Hashes.
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 με τελικό χαρακτήρα νέας γραμμής, όπως το echo συν PHP_EOL της PHP), τις υπογραφές υπορουτινών, και το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται σε όλον αυτόν τον οδηγό.
Πώς να το διαβάσετε αυτό#
Μεταβλητές και sigils - η μετάβαση
$/@/%.Συμβολοσειρές - παρεμβολή και συνένωση με
..Αριθμοί -
==έναντιeq, η κλασική παγίδα της PHP.Πίνακες και λίστες - το μισό από το
arrayσας.Hashes - το άλλο μισό από το
arrayσας.Περιβάλλον - η έννοια για την οποία η PHP δεν έχει λέξη. Διαβάστε αυτήν.
Αλήθεια - τι μετράει ως ψευδές, με εκπλήξεις.
Ροή ελέγχου -
if, βρόχοι, οι μεταθεματικές μορφές.Υπορουτίνες - το
functionγίνεταιsub.Αναφορές - εμφωλευμένα δεδομένα και closures.
Κανονικές εκφράσεις - το regex ως σύνταξη, όχι
preg_*.I/O αρχείων και ροών -
fopen, αναγνώσεις γραμμών, slurp.Αρθρώματα και πακέτα - τα
require/useγίνονταιuse.Αντικείμενα - η σύγχρονη σύνταξη
class.Χειρισμός σφαλμάτων -
try/catch, η ίδια ιδέα.Ιδιώματα - τα καθημερινά μονόγραμμα.
Παγίδες ερχόμενοι από την PHP - σαρώστε τες πριν γράψετε.
Μεταβλητές και sigils#
Χρησιμοποιείτε ήδη το $, οπότε το πρώτο μισό των sigils της Perl είναι δωρεάν. Η νέα ιδέα είναι ότι το sigil σημειώνει την προσπέλαση, όχι τη μεταβλητή. Όλος ο πίνακας είναι @a· ένα στοιχείο είναι $a[0]. Όλο το hash είναι %h· μία τιμή είναι $h{k}. Η PHP κρατάει το $ σε όλα και αλλάζει τις αγκύλες ($a[0], $h['k'])· η Perl αλλάζει το sigil αντ” αυτού. Το my δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, το αντίστοιχο μιας συνηθισμένης τοπικής σύνδεσης.
$name = "Ada";
$langs = ["PHP", "Perl"];
$ages = ["Ada" => 36, "Rasmus" => 56];
use v5.42;
my $name = "Ada";
my @langs = ("PHP", "Perl");
my %ages = (Ada => 36, Rasmus => 56);
say $name; # Ada
say $langs[0]; # PHP
say $ages{Ada}; # 36
say scalar @langs; # 2
Η παγίδα: όταν φτάνετε μέσα σε ένα σύνολο για ένα στοιχείο, το sigil ακολουθεί την τιμή που παίρνετε πίσω, όχι τον περιέκτη. Ένα μεμονωμένο στοιχείο του @langs είναι βαθμωτό, οπότε είναι $langs[0], όχι @langs[0]· μία τιμή από το %ages είναι $ages{Ada}. Διαβάστε το αρχικό sigil ως «τι σχήμα παίρνω» - $ για ένα πράγμα, @ για μια λίστα, % για όλη την αντιστοίχιση - και τις αγκύλες ως «από πού».
Συμβολοσειρές#
Η συνένωση είναι ., ακριβώς όπως στην PHP. Τα διπλά εισαγωγικά παρεμβάλλουν μεταβλητές, τα μονά όχι - ο ίδιος διαχωρισμός που κάνει η PHP ανάμεσα σε "..." και '...'. Δεν υπάρχει σύνταξη sprintf-μέσα-στη-συμβολοσειρά να μάθετε επειδή η προεπιλεγμένη συμβολοσειρά με διπλά εισαγωγικά ήδη παρεμβάλλει.
$name = "Ada";
$greeting = "Hello, $name";
$literal = 'Hello, $name';
$joined = "AB" . "CD";
$shout = str_repeat("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, όχι συνάρτηση - "AB" x 3, όχι str_repeat. Η παρεμβολή ενός στοιχείου πίνακα ή hash δεν χρειάζεται διαφυγή με άγκιστρα: τα "First: $langs[0]" και "Name: $h{name}" δουλεύουν και τα δύο όπως είναι γραμμένα, ενώ η PHP θα σας έβαζε να γράψετε "{$arr['k']}". Για να παρεμβάλετε το αποτέλεσμα μιας έκφρασης, τυλίξτε το σε @{[ ... ]}: το "sum: @{[ 2 + 3 ]}" τυπώνει sum: 5.
Αριθμοί#
Η Perl έχει έναν αριθμητικό τύπο στην επιφάνεια· δεν δηλώνετε int έναντι float. Αυτό που πραγματικά θα σας δαγκώσει είναι οι τελεστές, οπότε διαβάστε την παγίδα δύο φορές.
echo 7 / 2; // 3.5
echo intdiv(7, 2); // 3
echo 2 ** 10; // 1024
echo 10 % 3; // 1
use v5.42;
say 7 / 2; # 3.5
say int(7 / 2); # 3
say 2 ** 10; # 1024
say 10 % 3; # 1
Η παγίδα: η Perl έχει δύο οικογένειες τελεστών και επιλέγετε με βάση τον τύπο του τελεστέου, όχι με βάση τη μεταβλητή. Οι αριθμοί χρησιμοποιούν ==, !=, <, >, <=>· οι συμβολοσειρές χρησιμοποιούν eq, ne, lt, gt, cmp. Και το + είναι πάντα αριθμητικό - δεν υπάρχει υπερφόρτωση τελεστή όπως το + της PHP κάνει ένωση πινάκων. Συνενώστε με ., προσθέστε με +, και ποτέ μην τα διασταυρώσετε:
use v5.42;
say "12" . "3"; # 123 (string concat)
say "12" + "3"; # 15 (numeric add)
say "10" == "10.0" ? "same" : "diff"; # same (numeric compare)
say "10" eq "10.0" ? "same" : "diff"; # diff (string compare)
Η PHP απέσυρε το μπέρδεμα του χαλαρού == στις σύγχρονες εκδόσεις· η Perl δεν το είχε ποτέ, αλλά λύνει το ίδιο πρόβλημα κάνοντάς σας να πείτε ποια σύγκριση εννοείτε. Το "10" == "10.0" είναι αληθές (αριθμητικά), το "10" eq "10.0" είναι ψευδές (συμβολοσειρά). Η χρήση == σε συμβολοσειρές, ή eq σε αριθμούς, είναι το πιο συχνό σφάλμα κατά τη μετάβαση από PHP σε Perl.
Πίνακες και λίστες#
Εδώ είναι που ο ένας τύπος array της PHP χωρίζεται στα δύο. Όταν χρησιμοποιούσατε ένα array ως αριθμητικά ευρετηριασμένη λίστα - $items[0], array_push, foreach πάνω σε τιμές - αυτό είναι ένας πίνακας της Perl, που δηλώνεται με @. Τα ρήματα έχουν μετονομαστεί αλλά οι λειτουργίες είναι ίδιες, και οι αρνητικοί δείκτες δουλεύουν όπως περιμένετε.
$nums = [1, 2, 3, 4, 5];
$nums[] = 6;
$last = array_pop($nums);
echo count($nums); // 5
print_r(array_slice($nums, 1, 2)); // [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)
Τα array_map και array_filter σας γίνονται map και grep. Το map μετασχηματίζει (το array_map της PHP)· το grep φιλτράρει (το array_filter της PHP). Το $_ είναι το τρέχον στοιχείο, η σιωπηρή μεταβλητή βρόχου της Perl, όπως είναι η παράμετρος του callback στην PHP - μόνο που δεν το ονοματίζετε.
$squares = array_map(fn($x) => $x * $x, $nums);
$evens = array_filter($nums, fn($x) => $x % 2 == 0);
rsort($nums); // descending, in place
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 είναι το αδελφάκι του για συμβολοσειρές). Σημειώστε επίσης ότι το grep κρατάει στοιχεία, επιστρέφοντας μια νέα λίστα, ενώ το array_filter της PHP διατηρεί τα κλειδιά - οι πίνακες της Perl δεν έχουν κλειδιά να διατηρήσουν, οπότε το αποτέλεσμα απλώς ξανα-ευρετηριάζεται.
Hashes#
Το άλλο μισό του array της PHP - το μέρος που χρησιμοποιούσατε με κλειδιά συμβολοσειρών, $config['host'], array_key_exists, foreach ($h as $k => $v) - είναι ένα hash της Perl, που δηλώνεται με % και προσπελαύνεται μία τιμή τη φορά με $h{key}.
$h = ["apple" => 3, "pear" => 5, "plum" => 2];
echo $h["apple"]; // 3
$keys = array_keys($h);
sort($keys);
echo array_key_exists("pear", $h) ? "yes" : "no"; // yes
unset($h["plum"]);
foreach ($h as $k => $v) {
echo "$k => $v\n";
}
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}"; # apple => 3 ... pear => 5
}
Η παγίδα: ο έλεγχος μέλους είναι exists $h{key} (το κλειδί υπάρχει) ή defined $h{key} (το κλειδί υπάρχει και η τιμή του δεν είναι undef) - μαζί καλύπτουν τα array_key_exists και isset της PHP. Το παχύ κόμμα => είναι ο ιδιωματικός διαχωριστής κλειδιού/τιμής και βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του, οπότε το apple => 3 δεν χρειάζεται εισαγωγικά γύρω από το apple. Ένα hash της Perl δεν έχει σειρά εισαγωγής (σε αντίθεση με ένα array της PHP, που τη διατηρεί)· επαναλάβετε με sort keys %h όταν θέλετε μια σταθερή σειρά. Για να διατρέξετε κλειδί και τιμή μαζί, το foreach ($h as $k => $v) της PHP γίνεται είτε for my $k (keys %h) είτε, όταν θέλετε και τα δύο μαζί, while (my ($k, $v) = each %h).
Περιβάλλον#
Αυτό είναι το κεφάλαιο που πρέπει να διαβάσετε δύο φορές. Η PHP δεν έχει αντίστοιχο, και σχεδόν κάθε ιστορία «η 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 ($x, $y) = @list; # 10 and 20
say "$x $y"; # 10 20
Οι παρενθέσεις στα αριστερά είναι όλη η διαφορά. Το my $x = @list ρωτάει «πόσα;» και παίρνει 3 - εδώ ζει το count($arr), απλώς γραμμένο μέσω περιβάλλοντος αντί για συνάρτηση. Το my ($x) = @list ρωτάει «δώσε μου τα στοιχεία» και παίρνει 10. Ένας προγραμματιστής της PHP διαβάζει και τα δύο ως «ανάθεσε το array», που είναι ακριβώς η παγίδα.
Μια υπορουτίνα μπορεί να ρωτήσει σε ποιο περιβάλλον κλήθηκε, με 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 επιβάλλει βαθμωτό περιβάλλον για να πάρει ένα πλήθος - αυτό είναι το ρητό count() που ψάχνατε. Μια αντιστοίχιση regex με /g επιστρέφει τη λίστα των αντιστοιχιών σε περιβάλλον λίστας και ένα αληθές/ψευδές σε βαθμωτό περιβάλλον.
Η παγίδα: δεν υπάρχει μία μόνο απάντηση στο «τι κάνει αυτός ο πίνακας» - εξαρτάται από την περιβάλλουσα έκφραση. Όταν κάτι επιστρέφει έναν αριθμό που περιμένατε να είναι λίστα (ή το αντίστροφο), το περιβάλλον είναι ο πρώτος ύποπτος. Φτάστε στο scalar για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων.
Αλήθεια τιμών#
Οι ψευδείς τιμές της Perl είναι μια σύντομη, συγκεκριμένη λίστα: ο αριθμός 0, η κενή συμβολοσειρά "", η συμβολοσειρά "0", και το undef (το null της Perl). Όλα τα άλλα είναι αληθή. Αυτό είναι κοντά στους κανόνες της PHP αλλά όχι πανομοιότυπο, και οι διαφορές είναι σιωπηλές.
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, παρόλο που η PHP αντιμετωπίζει το "0" και μόνο το "0" ως την ψευδή συμβολοσειρά επίσης - η Perl συμφωνεί στο "0" αλλά όχι στα όμοιά του. Η συμβολοσειρά "false" είναι αληθής (είναι μια μη κενή, μη-"0" συμβολοσειρά), το ίδιο όπως στην PHP. Και σε αντίθεση με τον κανόνα της PHP ότι το κενό array είναι ψευδές, ένας κενός πίνακας της Perl δεν είναι ο ίδιος μια ψευδής τιμή: σε λογικό περιβάλλον ένας πίνακας δίνει το μήκος του, οπότε το if (@list) είναι αληθές ακριβώς όταν η λίστα είναι μη κενή, που είναι ο έλεγχος που θέλετε. Για το «χρησιμοποίησε αυτό αν είναι ορισμένο, αλλιώς μια προεπιλογή», χρησιμοποιήστε το defined-or //, το αντίστοιχο του ?? της PHP, που πέφτει πίσω μόνο στο undef (όχι στο 0 ή στο ""):
use v5.42;
my %cfg = (host => "localhost");
my $port = $cfg{port} // 8080; # key absent -> undef -> 8080
say $port; # 8080
Ροή ελέγχου#
Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει με δύο μικρούς τρόπους. Το elseif είναι elsif (ένα e), και οι συνθήκες χρειάζονται παρενθέσεις ενώ τα μπλοκ χρειάζονται άγκιστρα - δεν υπάρχει εναλλακτική σύνταξη :/endif.
if ($n < 0) {
$sign = "neg";
} elseif ($n == 0) {
$sign = "zero";
} else {
$sign = "pos";
}
foreach ($nums as $x) {
echo $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 προσθέτει δύο πράγματα που δεν έχει η PHP. Οι μεταθεματικοί τροποποιητές επιτρέπουν σε μια απλή εντολή να φέρει τη δική της συνθήκη ή βρόχο, κάτι που διαβάζεται φυσικά για μονόγραμμα:
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.
Η παγίδα: ο τριαδικός τελεστής είναι ?/:, ο ίδιος με της PHP, και αλυσιδώνεται με τον ίδιο τρόπο: my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos". Η Perl δεν έχει εντολή switch/match στον πυρήνα· ένας αλυσιδωτός τριαδικός τελεστής ή ένα hash αποστολής ($handler{$key}->()) είναι το ιδίωμα, και ένα μπλοκ for-πάνω-σε-μία-τιμή είναι το συνηθισμένο υποκατάστατο για ένα switch.
Υπορουτίνες#
Το function της PHP γίνεται το sub της Perl. Η σύγχρονη Perl έχει υπογραφές που μοιάζουν πολύ με τις λίστες παραμέτρων της PHP, συμπεριλαμβανομένων των προεπιλεγμένων τιμών.
function greet($name, $greeting = "Hello") {
return "$greeting, $name!";
}
function total(...$nums) {
return array_sum($nums);
}
use v5.42;
sub greet ($name, $greeting = "Hello") {
return "$greeting, $name!";
}
say greet("Ada"); # Hello, Ada!
say greet("Rasmus", "Hi"); # Hi, Rasmus!
sub total (@nums) {
my $sum = 0;
$sum += $_ for @nums;
return $sum;
}
say total(1, 2, 3, 4); # 10
Το variadic ...$nums γίνεται μια τελική παράμετρος πίνακα @nums, που συγκεντρώνει όλα τα υπόλοιπα ορίσματα. Τα ονοματισμένα ορίσματα της PHP (ή η συνηθισμένη σύμβαση $opts με array) αντιστοιχίζονται σε μια παράμετρο 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, ο καλών παίρνει τη λίστα σε περιβάλλον λίστας αλλά το πλήθος σε βαθμωτό περιβάλλον. Να είστε εσκεμμένοι σχετικά με το τι επιστρέφετε όταν μπορεί να χρησιμοποιηθεί με οποιονδήποτε τρόπο.
Αναφορές#
Οι πίνακες της PHP εμφωλεύονται απευθείας: ένα array μπορεί να κρατήσει ένα άλλο array και απλώς συνεχίζετε να βάζετε δείκτες. Οι πίνακες και τα hashes της Perl δεν εμφωλεύονται απευθείας· τα εμφωλεύετε μέσω αναφορών, βαθμωτών που δείχνουν σε ένα σύνολο. Αυτός είναι ο ίδιος μηχανισμός με τις αναφορές &$x της PHP και τις λαβές αντικειμένων της, αλλά στην Perl τον χρησιμοποιείτε κάθε φορά που χτίζετε εμφωλευμένα δεδομένα. Πάρτε μια αναφορά με \, ακολουθήστε τη με ->.
$data = ["name" => "Ada", "langs" => ["Perl", "PHP"]];
echo $data["langs"][1]; // PHP
$data["langs"][] = "Ruby";
use v5.42;
my $data = { name => "Ada", langs => ["Perl", "PHP"] };
say $data->{name}; # Ada
say $data->{langs}[1]; # PHP
push $data->{langs}->@*, "Ruby"; # postfix deref to push
say "@{$data->{langs}}"; # Perl PHP Ruby
Το { ... } φτιάχνει μια ανώνυμη αναφορά hash και το [ ... ] μια ανώνυμη αναφορά πίνακα - τα κυριολεκτικά δομικά στοιχεία των εμφωλευμένων δεδομένων, που αντιστοιχούν στις κυριολεξίες [...] και [k => v] που ήδη γράφετε στην PHP. Το βέλος -> ακολουθεί μια αναφορά κατά ένα βήμα· ανάμεσα σε δύο δείκτες είναι προαιρετικό, οπότε τα $data->{langs}[1] και $data->{langs}->[1] είναι ίδια. Το βέλος είναι επίσης ακριβώς το βέλος κλήσης μεθόδου που ξέρετε από την αντικειμενοστρέφεια της PHP - ίδιο σύμβολο, και για το «φτάσε μέσα» και για το «κάλεσε μια μέθοδο».
Οι ανώνυμες subs είναι closures, ακριβώς όπως τα fn και function() use (...) της PHP, αλλά χωρίς να χρειάζεται ρήτρα use - συλλαμβάνουν αυτόματα τις λεξιλογικές μεταβλητές:
use v5.42;
my $double = sub ($n) { $n * 2 };
say $double->(21); # 42
Η παγίδα: οι μεταθεματικές μορφές αποαναφοράς (->@*, ->%*, ->$*) είναι ο σύγχρονος, ευανάγνωστος τρόπος να αποαναφέρετε ένα ολόκληρο σύνολο, και το "@{ ... }" είναι ο τρόπος να παρεμβάλετε έναν αποαναφερμένο πίνακα μέσα σε μια συμβολοσειρά. Οι παλαιότερες μορφές @$ref και ${$ref}{key} εξακολουθούν να δουλεύουν και εμφανίζονται σε υπάρχοντα κώδικα, αλλά προτιμήστε το βέλος και τις μεταθεματικές μορφές σε νέο κώδικα.
Κανονικές εκφράσεις#
Στην PHP το regex είναι η οικογένεια συναρτήσεων preg_* με συμβολοσειρές μοτίβων όπως '/\d+/'. Στην Perl είναι σύνταξη: το =~ συνδέει ένα μοτίβο με μια συμβολοσειρά, το m// αντιστοιχίζει, το s/// αντικαθιστά - χωρίς οριοθέτες-μέσα-σε-συμβολοσειρά, χωρίς κλήση συνάρτησης. Οι ομάδες σύλληψης καταλήγουν στα $1, $2, και οι ονομαστικές συλλήψεις στο %+.
if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $s, $m)) {
[$_, $year, $month, $day] = $m;
}
$t = preg_replace('/,/', ';', "a,b,c");
$parts = explode(",", "one,two,three");
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 σε περιβάλλον λίστας επιστρέφει όλες τις συλλήψεις, το άμεσο αντίστοιχο του preg_match_all:
use v5.42;
my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3
say "@nums";
Η παγίδα: το s/// τροποποιεί τον στόχο του επιτόπια και επιστρέφει τον αριθμό των αντικαταστάσεων, όχι μια νέα συμβολοσειρά - σε αντίθεση με το preg_replace, που επιστρέφει μια φρέσκια συμβολοσειρά. Γι” αυτό το παράδειγμα αντιγράφει πρώτα με (my $t = "a,b,c") και μετά αντικαθιστά στο αντίγραφο - αλλιώς θα επεξεργαζόσασταν μια κυριολεξία συμβολοσειράς. Για να πάρετε μια νέα συμβολοσειρά χωρίς να αγγίξετε την αρχική (το αντανακλαστικό preg_replace), χρησιμοποιήστε τη σημαία /r: my $t = $s =~ s/,/;/gr.
I/O αρχείων και ροών#
Το ιδίωμα είναι το open με τρία ορίσματα και ένα λεξιλογικό filehandle, το αντίστοιχο του fopen της PHP μέσα σε ένα $handle. Ο τελεστής με γωνιώδεις αγκύλες <$fh> διαβάζει μια γραμμή, με τον ίδιο ρόλο όπως το fgets.
$wh = fopen("/tmp/sample.txt", "w");
for ($i = 1; $i <= 3; $i++) {
fwrite($wh, "line $i\n");
}
fclose($wh);
$rh = fopen("/tmp/sample.txt", "r");
while (($line = fgets($rh)) !== false) {
echo "got: " . rtrim($line) . "\n";
}
fclose($rh);
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 σε περιβάλλον λίστας - το ηθικό αντίστοιχο του file():
use v5.42;
open my $fh, '<', "/tmp/sample.txt" or die $!;
my @lines = <$fh>; # all lines, including newlines
say scalar @lines; # 3
Η παγίδα: μια γραμμή που διαβάστηκε με <$fh> κρατάει τον τελικό χαρακτήρα νέας γραμμής· καλέστε chomp για να τον αφαιρέσετε (το αντανακλαστικό rtrim, αλλά το chomp αφαιρεί μόνο τον διαχωριστή εγγραφών). Και το open επιστρέφει ψευδές σε αποτυχία αντί να εγείρει εξαίρεση ή να προειδοποιεί, οπότε το ιδίωμα or die "...: $!" είναι υποχρεωτικό - το $! είναι το μήνυμα σφάλματος συστήματος, το errno της Perl (το error_get_last() της PHP).
Αρθρώματα και πακέτα#
Τα require/include της PHP και ο autoloader του Composer γίνονται το use της Perl. Ο διαχωριστής χώρου ονομάτων είναι το ::, όχι το \, και ένα άρθρωμα Foo::Bar βρίσκεται στο Foo/Bar.pm στη διαδρομή ένταξης @INC (το include_path της Perl). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων που θα φέρετε στον χώρο ονομάτων σας.
require 'vendor/autoload.php';
use App\Util\ListHelper;
$sum = ListHelper::sum(range(1, 10));
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
Ο τεράστιος επίπεδος χώρος ονομάτων τυπικών συναρτήσεων της PHP (count, in_array, array_map, implode, sprintf, …) αντιστοιχίζεται στην Perl εν μέρει σε ενσωματωμένους τελεστές και εν μέρει σε λίγα καθημερινά αρθρώματα. Οι συνηθισμένες μεταφράσεις:
PHP | Perl |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Τα List::Util, Scalar::Util, POSIX, File::Spec, και Data::Dumper είναι τα καθημερινά αρθρώματα· τα περισσότερα από αυτά που θα τραβούσατε από το Packagist έρχονται στον πυρήνα ή στο CPAN, το αρχείο πακέτων της Perl.
Η παγίδα: το use τρέχει κατά τη μεταγλώττιση, όχι στο σημείο του αρχείου όπου εμφανίζεται, οπότε η σειρά των γραμμών use σε σχέση με τον κώδικά σας δεν έχει σημασία όπως μπορεί να έχει η τοποθέτηση του require στην PHP. Η εισαγωγή είναι επίσης προαιρετική ανά όνομα: το use List::Util qw(sum0 max) φέρνει ακριβώς τα sum0 και max στον χώρο ονομάτων σας, τίποτε άλλο - δεν υπάρχει καθολικός πίνακας συναρτήσεων όπως η PHP εκθέτει κάθε ενσωματωμένη παντού.
(Τα δεδομένα αιτήματος web - τα superglobals $_GET, $_POST, $_SERVER της PHP - δεν έχουν αντίστοιχο σε επίπεδο γλώσσας στην Perl· αυτή είναι η δουλειά ενός επιπέδου CGI ή PSGI, όχι της βασικής γλώσσας, και είναι εκτός σκοπού για αυτόν τον οδηγό.)
Αντικείμενα#
Για νέο κώδικα, η Perl 5.42 έχει μια σύνταξη class πρώτης τάξης που θα φανεί οικεία ερχόμενοι από την PHP. Τα πεδία δηλώνονται με field, οι μέθοδοι με method, και το $self είναι διαθέσιμο μέσα σε κάθε μέθοδο ακριβώς όπως το $this στην PHP (δεν το αναφέρετε ως παράμετρο).
class Point {
public function __construct(
public int $x = 0,
public int $y = 0,
) {}
public function to_string(): string {
return "({$this->x}, {$this->y})";
}
public function move(int $dx, int $dy): static {
$this->x += $dx;
$this->y += $dy;
return $this;
}
}
$p = new Point(x: 3, y: 4);
echo $p->to_string(); // (3, 4)
$p->move(1, 1);
echo $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)
Ο κατασκευαστής δημιουργείται για εσάς, όπως οι προαγόμενες ιδιότητες κατασκευαστή της PHP: το :param σημειώνει ένα πεδίο που η αυτόματα κατασκευασμένη new δέχεται ως ονοματισμένο όρισμα, και το = 0 είναι η προεπιλογή του. Το βέλος κλήσης μεθόδου -> είναι το ίδιο που χρησιμοποιείτε στην PHP. Η κληρονομικότητα είναι :isa, το αντίστοιχο του extends:
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 συμπεριφέρεται ακριβώς όπως φαίνεται παραπάνω· απλώς αναμένετε τις ειδοποιήσεις στη ροή σφαλμάτων. Το παλαιότερο μοντέλο αντικειμένων - ένα πακέτο, ένα hash που έχει γίνει bless σε αυτό με bless, και το @ISA για κληρονομικότητα - είναι αυτό που θα συναντήσετε σε υπάρχοντα κώδικα και στο μεγαλύτερο μέρος του CPAN· γι” αυτό, για ρόλους, και για συμβουλές μετάβασης, δείτε τον οδηγό Αντικειμενοστραφούς Προγραμματισμού.
Χειρισμός σφαλμάτων#
Το try/catch της PHP έχει ένα άμεσο αντίστοιχο στη σύγχρονη Perl, και διαβάζεται σχεδόν πανομοιότυπα. Η πλευρά που εγείρει είναι το die (το throw της PHP):
function risky($n) {
if ($n < 0) {
throw new Exception("negative!");
}
return sqrt($n);
}
try {
risky(-4);
} catch (Exception $e) {
echo "caught: " . $e->getMessage() . "\n";
}
use v5.42;
use feature 'try';
sub risky ($n) {
die "negative!\n" if $n < 0;
return sqrt $n;
}
try {
risky(-4);
}
catch ($e) {
print "caught: $e"; # caught: negative!
}
Η παλαιότερη, ακόμη συνηθισμένη μορφή τυλίγει κώδικα σε eval { } και εξετάζει το $@ (το σφάλμα που πιάστηκε) μετά - το ίδιο μοτίβο με κώδικα PHP γραμμένο πριν οι εξαιρέσεις γίνουν ιδιωματικές:
use v5.42;
my $result = eval { risky(-1) };
if ($@) {
print "caught: $@"; # caught: negative!
}
Η παγίδα: το die με μια συμβολοσειρά που τελειώνει σε \n παράγει ακριβώς αυτό το μήνυμα· το die με μια συμβολοσειρά που δεν τελειώνει σε χαρακτήρα νέας γραμμής παίρνει αυτόματα την προσθήκη " at SCRIPT line N.\n". Τελειώστε τις συμβολοσειρές σφάλματός σας με \n όταν θέλετε ένα καθαρό μήνυμα και παραλείψτε το όταν θέλετε το ίχνος αρχείου-και-γραμμής. Σημειώστε επίσης τις δύο μεταβλητές σφάλματος: το $@ είναι η τελευταία εξαίρεση που πιάστηκε (το $e της PHP), ενώ το $! είναι το τελευταίο σφάλμα συστήματος (ένα αποτυχημένο open, η συμβολοσειρά errno).
Ιδιωματισμοί#
Μερικά μοτίβα που θα βλέπετε καθημερινά και θα θέλετε στα δάχτυλά σας.
Ταξινομήστε μια λίστα με βάση ένα υπολογισμένο κλειδί (ο Schwartzian transform - το usort της PHP με ένα προϋπολογισμένο κλειδί, γινόμενο σε μία έκφραση: διακόσμησε, ταξινόμησε, αφαίρεσε τη διακόσμηση):
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 από μια λίστα, το αντίστοιχο του array_combine/array_column (εδώ, λέξη προς το μήκος της):
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 - την ίδια δουλειά για την οποία ίσως κατέφευγες σε ένα γρήγορο σενάριο PHP CLI. Το μονόγραμμο με σιωπηρό βρόχο είναι το καθημερινό αντανακλαστικό του προγραμματιστή της Perl:
pperl -lane '$s += $F[-1]; END { print $s }' data.txt
Η παγίδα: διαβάστε τον οδηγό Μονόγραμμων για τους διακόπτες (-n, -p, -a, -l, -e) και τις δεκάδες συνταγές που είναι χτισμένες πάνω τους· είναι ένα από τα πράγματα με τη μεγαλύτερη μόχλευση που μπορείτε να μάθετε ερχόμενοι από υπόβαθρο PHP-και-κελύφους.
Παγίδες ερχόμενοι από την PHP#
Οι παγίδες, συγκεντρωμένες για μια τελική σάρωση πριν αρχίσετε να γράφετε:
Τα sigils αλλάζουν με τη χρήση. Ένα στοιχείο του
@aείναι$a[0]· μία τιμή του%hείναι$h{k}. Η PHP κρατάει το$παντού· η Perl αλλάζει το sigil ανάλογα με το σχήμα που παίρνετε.Ένα
arrayτης PHP είναι δύο τύποι στην Perl. Αποφασίστε εξαρχής: ακέραια κλειδιά και σειρά σημαίνει@array· κλειδιά συμβολοσειρών σημαίνει%hash. Δεν είναι εναλλάξιμα.Δύο οικογένειες σύγκρισης.
==/</<=>για αριθμούς,eq/lt/cmpγια συμβολοσειρές. Επιλέξτε με βάση τον τύπο του τελεστέου, όχι με βάση τη μεταβλητή. Το==σε συμβολοσειρές ή τοeqσε αριθμούς είναι το κλασικό σφάλμα.Το
.ενώνει συμβολοσειρές, το+προσθέτει μόνο αριθμούς. Το+της Perl ποτέ δεν κάνει ένωση πινάκων ή συνένωση· το μπέρδεμα.και+αλλοιώνει τα δεδομένα σας σιωπηλά ή προειδοποιεί.Το
sortταξινομεί ως συμβολοσειρές από προεπιλογή. Περάστε{ $a <=> $b }για αριθμητική σειρά.Τα hashes δεν έχουν σειρά. Σε αντίθεση με ένα array της PHP, η σειρά επανάληψης δεν είναι η σειρά εισαγωγής· κάντε
sort keys %hόταν χρειάζεστε σταθερότητα.Περιβάλλον. Το
my $x = @aείναι το πλήθος· τοmy ($x) = @aείναι το πρώτο στοιχείο. Οι παρενθέσεις είναι όλη η διαφορά. Ξαναδιαβάστε το Περιβάλλον όταν μια τιμή επιστρέφει με λάθος σχήμα.Εκπλήξεις αλήθειας. Το
"0"είναι ψευδές, αλλά τα"00"και"0.0"είναι αληθή. Μόνο τα0,"","0", καιundefείναι ψευδή. Ένας κενός πίνακας δεν είναι ψευδής τιμή - τοif (@a)ελέγχει αν είναι μη κενός.Το
s///επεξεργάζεται επιτόπια και επιστρέφει ένα πλήθος, σε αντίθεση με τοpreg_replace· χρησιμοποιήστε τη σημαία/rγια να πάρετε μια νέα συμβολοσειρά αντ” αυτού.Το
openδεν εγείρει εξαίρεση. Χρησιμοποιήστεopen ... or die "...: $!".Κάντε
chompστις αναγνώσεις γραμμών σας. Το<$fh>κρατάει τον τελικό χαρακτήρα νέας γραμμής.Τα superglobals του web δεν ανήκουν στον πυρήνα. Τα
$_GET/$_POSTανήκουν σε ένα επίπεδο CGI ή PSGI, όχι στη γλώσσα.Η μη-ASCII πηγή χρειάζεται
use utf8;. Υπόuse v5.42;ένα κυριολεκτικό μη-ASCII byte στην πηγή σας είναι σφάλμα μεταγλώττισης χωρίς αυτό. Κρατήστε τα δεδομένα συμβολοσειρών ASCII εκτός αν προσθέσετε ρητάuse utf8;.The
classfeature prints experimental notices to stderr onpperl. Correct output, noisy error stream.