# Από 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: ```perl 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` δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, το αντίστοιχο μιας συνηθισμένης τοπικής σύνδεσης. ```php $name = "Ada"; $langs = ["PHP", "Perl"]; $ages = ["Ada" => 36, "Rasmus" => 56]; ``` ```perl 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`-μέσα-στη-συμβολοσειρά να μάθετε επειδή η προεπιλεγμένη συμβολοσειρά με διπλά εισαγωγικά ήδη παρεμβάλλει. ```php $name = "Ada"; $greeting = "Hello, $name"; $literal = 'Hello, $name'; $joined = "AB" . "CD"; $shout = str_repeat("AB", 3); ``` ```perl 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`. Αυτό που πραγματικά θα σας δαγκώσει είναι οι τελεστές, οπότε διαβάστε την παγίδα δύο φορές. ```php echo 7 / 2; // 3.5 echo intdiv(7, 2); // 3 echo 2 ** 10; // 1024 echo 10 % 3; // 1 ``` ```perl 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 κάνει ένωση πινάκων. Συνενώστε με `.`, προσθέστε με `+`, και ποτέ μην τα διασταυρώσετε: ```perl 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, που δηλώνεται με `@`. Τα ρήματα έχουν μετονομαστεί αλλά οι λειτουργίες είναι ίδιες, και οι αρνητικοί δείκτες δουλεύουν όπως περιμένετε. ```php $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] ``` ```perl 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 - μόνο που δεν το ονοματίζετε. ```php $squares = array_map(fn($x) => $x * $x, $nums); $evens = array_filter($nums, fn($x) => $x % 2 == 0); rsort($nums); // descending, in place ``` ```perl 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}`. ```php $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"; } ``` ```perl 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 τρέχει είτε σε **περιβάλλον λίστας** είτε σε **βαθμωτό περιβάλλον**, που καθορίζεται από το πού εμφανίζεται, όχι από το τι είναι. Ο ίδιος πίνακας συμπεριφέρεται διαφορετικά σε καθένα. Η ανάθεση ενός πίνακα σε ένα *βαθμωτό* δίνει το μήκος του· η ανάθεσή του σε μια *λίστα* (ένα σύνολο μεταβλητών σε παρενθέσεις) αντιγράφει στοιχεία. ```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`, και να επιστρέψει διαφορετικά πράγματα ανάλογα: ```perl 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 αλλά όχι πανομοιότυπο, και οι διαφορές είναι σιωπηλές. ```perl use v5.42; for my $v (0, 1, "", "0", "00", "0.0", "false") { say "'$v' is ", ($v ? "true" : "false"); } ``` Αυτό τυπώνει, ακριβώς: ```text '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` ή στο `""`): ```perl use v5.42; my %cfg = (host => "localhost"); my $port = $cfg{port} // 8080; # key absent -> undef -> 8080 say $port; # 8080 ``` ## Ροή ελέγχου Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει με δύο μικρούς τρόπους. Το `elseif` είναι `elsif` (ένα `e`), και οι συνθήκες χρειάζονται παρενθέσεις ενώ τα μπλοκ χρειάζονται άγκιστρα - δεν υπάρχει εναλλακτική σύνταξη `:`/`endif`. ```php if ($n < 0) { $sign = "neg"; } elseif ($n == 0) { $sign = "zero"; } else { $sign = "pos"; } foreach ($nums as $x) { echo $x; } while ($running) { step(); } ``` ```perl 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. Οι **μεταθεματικοί τροποποιητές** επιτρέπουν σε μια απλή εντολή να φέρει τη δική της συνθήκη ή βρόχο, κάτι που διαβάζεται φυσικά για μονόγραμμα: ```perl 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, συμπεριλαμβανομένων των προεπιλεγμένων τιμών. ```php function greet($name, $greeting = "Hello") { return "$greeting, $name!"; } function total(...$nums) { return array_sum($nums); } ``` ```perl 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` συγκεντρώνει: ```perl 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 τον χρησιμοποιείτε κάθε φορά που χτίζετε εμφωλευμένα δεδομένα. Πάρτε μια αναφορά με `\`, ακολουθήστε τη με `->`. ```php $data = ["name" => "Ada", "langs" => ["Perl", "PHP"]]; echo $data["langs"][1]; // PHP $data["langs"][] = "Ruby"; ``` ```perl 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` - συλλαμβάνουν αυτόματα τις λεξιλογικές μεταβλητές: ```perl 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`, και οι ονομαστικές συλλήψεις στο `%+`. ```php 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"); ``` ```perl 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 =~ /(?\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`: ```perl 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`. ```php $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); ``` ```perl 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()`: ```perl 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). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων που θα φέρετε στον χώρο ονομάτων σας. ```php require 'vendor/autoload.php'; use App\Util\ListHelper; $sum = ListHelper::sum(range(1, 10)); ``` ```perl 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 | |------------------------|------------------------------------| | `count($a)` | `scalar @a` | | `in_array($x, $a)` | `grep { $_ eq $x } @a` | | `array_map($f, $a)` | `map { ... } @a` | | `array_filter($a, $f)` | `grep { ... } @a` | | `implode(",", $a)` | `join ",", @a` | | `explode(",", $s)` | `split /,/, $s` | | `sprintf(...)` | `sprintf(...)` (ίδιο) | | `isset($h[$k])` | `exists $h{$k}` / `defined $h{$k}` | | `array_sum($a)` | `sum0 @a` (`List::Util`) | Τα `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 (δεν το αναφέρετε ως παράμετρο). ```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) ``` ```perl 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`: ```perl 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· γι” αυτό, για ρόλους, και για συμβουλές μετάβασης, δείτε τον [οδηγό Αντικειμενοστραφούς Προγραμματισμού](../oop/index.md). ## Χειρισμός σφαλμάτων Το `try`/`catch` της PHP έχει ένα άμεσο αντίστοιχο στη σύγχρονη Perl, και διαβάζεται σχεδόν πανομοιότυπα. Η πλευρά που εγείρει είναι το `die` (το `throw` της PHP): ```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"; } ``` ```perl 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 γραμμένο πριν οι εξαιρέσεις γίνουν ιδιωματικές: ```perl 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 με ένα προϋπολογισμένο κλειδί, γινόμενο σε μία έκφραση: διακόσμησε, ταξινόμησε, αφαίρεσε τη διακόσμηση): ```perl 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` (εδώ, λέξη προς το μήκος της): ```perl 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: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` Η παγίδα: διαβάστε τον [οδηγό Μονόγραμμων](../oneliner/index.md) για τους διακόπτες (`-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 `class` feature prints experimental notices to stderr** on `pperl`. Correct output, noisy error stream.