# Από Lua σε Perl Ξέρετε Lua. Χτίζετε ένα table χωρίς να το σκέφτεστε, φτάνετε στο `setmetatable` για να πάρετε αντικείμενα και τελεστές, και το `for k, v in pairs(t)` είναι μυϊκή μνήμη. Αυτός ο οδηγός αντιστοιχίζει καθεμία από αυτές τις συνήθειες στην Perl, ώστε να γίνετε παραγωγικοί στο `pperl` μέσα σε ένα απόγευμα αντί να ξαναμάθετε προγραμματισμό από το μηδέν. Δύο σοκ φτάνουν μέσα στα πρώτα πέντε λεπτά, οπότε γνωρίστε τα τώρα: - **Ο ένας τύπος table της Lua γίνεται δύο τύποι στην Perl.** Η Lua έχει ένα μοναδικό σύνολο, το table, που το χρησιμοποιείτε και ως πίνακα (`{1, 2, 3}`) και ως αντιστοίχιση (`{k = "v"}`). Η Perl το χωρίζει σε δύο διακριτά πράγματα: έναν διατεταγμένο `@array` με ακέραια κλειδιά και ένα `%hash` με κλειδιά συμβολοσειρών. Η απόφαση «είναι αυτό λίστα ή αναζήτηση;» εξαρχής είναι η κεντρική νέα συνήθεια, και οι ενότητες [Πίνακες]() και [Hashes]() δεν αφορούν τίποτε άλλο. - **Οι μεταβλητές φορούν sigils.** Τα ονόματα της Lua είναι σκέτα: `x`, `t`, `f`. Στην Perl ένα βαθμωτό είναι `$x`, ένας πίνακας είναι `@x`, ένα hash είναι `%x`. Το sigil είναι μέρος του πώς διαβάζετε τη μεταβλητή, όχι απλώς διακόσμηση. Αυτή είναι η μεγαλύτερη επιφανειακή διαφορά από τη Lua, και η πρώτη ενότητα δεν αφορά τίποτε άλλο. 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` (κάθε μεταβλητή πρέπει να δηλώνεται με `my`, η θεραπεία για τις επιρρεπείς σε ατυχήματα σιωπηρές καθολικές της Lua), τα `warnings`, την ενσωματωμένη `say` (ένα `print` με τελικό χαρακτήρα νέας γραμμής, όπως το `print` της Lua), τις υπογραφές υπορουτινών, και το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται σε όλον αυτόν τον οδηγό. ## Πώς να το διαβάσετε αυτό - [Μεταβλητές και sigils]() - η μετάβαση `$`/`@`/`%`, και το `local x` που γίνεται `my $x`. - [Συμβολοσειρές]() - παρεμβολή, και η συνένωση `..` που γίνεται `.`. - [Αριθμοί]() - ένας επιφανειακός τύπος, διαίρεση κινητής υποδιαστολής. - [Πίνακες και λίστες]() - το μισό από το table σας, 0-based όχι 1-based. - [Hashes]() - το άλλο μισό από το table σας. - [Περιβάλλον]() - η έννοια για την οποία η Lua δεν έχει λέξη. Διαβάστε αυτήν. - [Αλήθεια]() - τι μετράει ως ψευδές, και διαφέρει έντονα από τη Lua. - [Ροή ελέγχου]() - `if`, βρόχοι, οι μεταθεματικές μορφές. - [Υπορουτίνες]() - το `function` γίνεται `sub`· πραγματικά closures. - [Αναφορές]() - εμφωλευμένα δεδομένα και closures. - [Κανονικές εκφράσεις]() - πραγματικό regex, όχι μοτίβα της Lua. - [I/O αρχείων και ροών]() - `open`, αναγνώσεις γραμμών, slurp. - [Αρθρώματα και πακέτα]() - το `require` γίνεται `use`. - [Αντικείμενα]() - τα metatables γίνονται η σύγχρονη σύνταξη `class`. - [Χειρισμός σφαλμάτων]() - το `pcall` γίνεται `eval` ή `try`/`catch`. - [Ιδιώματα]() - τα καθημερινά μονόγραμμα. - [Παγίδες ερχόμενοι από τη Lua]() - σαρώστε τες πριν γράψετε. ## Μεταβλητές και sigils Στη Lua ένα όνομα απλώς ονοματίζει μια τιμή, όποιο κι αν είναι το σχήμα της. Στην Perl το **sigil σας λέει το σχήμα**: `$` για μία τιμή, `@` για μια διατεταγμένη λίστα, `%` για μια αντιστοίχιση κλειδιού/τιμής. Το `my` δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, τον ακριβή ρόλο του `local` της Lua. Εκεί που η Lua αφήνει μια σκέτη ανάθεση να δημιουργήσει σιωπηλά μια καθολική, το `use v5.42` το μετατρέπει σε σφάλμα μεταγλώττισης: κάθε μεταβλητή είναι `my` εκτός αν πείτε διαφορετικά. ```lua local name = "Ada" local langs = {"Lua", "Perl"} local ages = {Ada = 36, Linus = 56} ``` ```perl use v5.42; my $name = "Ada"; my @langs = ("Lua", "Perl"); my %ages = (Ada => 36, Linus => 56); say $name; # Ada say $langs[0]; # Lua say $ages{Ada}; # 36 ``` Η παγίδα: όταν φτάνετε *μέσα* σε ένα σύνολο για ένα στοιχείο, το sigil ακολουθεί την τιμή που παίρνετε πίσω, όχι τον περιέκτη. Ένα μεμονωμένο στοιχείο του `@langs` είναι βαθμωτό, οπότε είναι `$langs[0]`, όχι `@langs[0]`· μία τιμή από το `%ages` είναι `$ages{Ada}`. Η μορφή `@`/`%` είναι όλη η συλλογή· η μορφή `$` είναι ένα κομμάτι της. Στη Lua το όνομα table `t` δεν αλλάζει ποτέ είτε γράφετε `t`, `t[1]`, ή `t.key`· στην Perl το αρχικό σημάδι αλλάζει ανάλογα με το τι προσπελαύνετε. ## Συμβολοσειρές Τα διπλά εισαγωγικά παρεμβάλλουν μεταβλητές, τα μονά όχι. Δεν χρειάζεται κανένα αντανακλαστικό `string.format` για τη συνηθισμένη περίπτωση, επειδή η προεπιλεγμένη συμβολοσειρά ήδη παρεμβάλλει. Η μεγάλη αλλαγή: η συνένωση είναι `.`, εκεί που η Lua χρησιμοποιεί `..`. ```lua local name = "Ada" local greeting = "Hello, " .. name local shout = string.rep("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 ``` Η παγίδα: ο τελεστής συνένωσης `..` της Lua δεν υπάρχει στην Perl· η μονή τελεία `.` ενώνει συμβολοσειρές και ο τελεστής `x` τις επαναλαμβάνει (το `string.rep` της Lua). Τα στοιχεία πίνακα και οι τιμές hash παρεμβάλλονται και μέσα σε διπλά εισαγωγικά, οπότε το `"First: $langs[0]"` δουλεύει όπως είναι γραμμένο, που εξαλείφει το μεγαλύτερο μέρος της αλυσίδωσης με `..` στην οποία καταφεύγει ένας προγραμματιστής της Lua. ## Αριθμοί Η Perl έχει έναν αριθμητικό τύπο στην επιφάνεια· δεν επιλέγετε μεταξύ ακεραίου και float, όπως ακριβώς η Lua 5.3+ παρουσιάζει έναν μοναδικό `number` στον περισσότερο κώδικα. Η διαίρεση πάντα παράγει έναν αριθμό κινητής υποδιαστολής. ```lua print(7 / 2) -- 3.5 print(math.floor(7 / 2)) -- 3 print(2 ^ 10) -- 1024.0 print(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 ``` Η παγίδα: οι τελεστές σύγκρισης και ισότητας έρχονται σε δύο οικογένειες, και επιλέγετε με βάση τον *τύπο των τελεστέων*, όχι των μεταβλητών. Οι αριθμοί χρησιμοποιούν `==`, `!=`, `<`, `>`, `<=>`· οι συμβολοσειρές χρησιμοποιούν `eq`, `ne`, `lt`, `gt`, `cmp`. Το `"10" == "10.0"` είναι αληθές (αριθμητικά), αλλά το `"10" eq "10.0"` είναι ψευδές (συμβολοσειρά). Η Lua έχει μόνο το `==` και δεν εξαναγκάζει τίποτε πέρα από τη γραμμή συμβολοσειράς/αριθμού· η Perl αντ” αυτού ζητάει από *εσάς* να επιλέξετε τη σύγκριση, και η χρήση `==` σε συμβολοσειρές (ή `eq` σε αριθμούς) είναι ένα συχνό σιωπηλό σφάλμα. ## Πίνακες και λίστες Εδώ είναι το μισό του μεγάλου διαχωρισμού του table. Μια ακολουθία της Lua (`{10, 20, 30}`, το table χρησιμοποιούμενο ως πίνακας) γίνεται ένας `@array` της Perl. Η πρώτη και πιο εμφανής διαφορά: **οι πίνακες της Perl είναι 0-based**, εκεί που οι πίνακες της Lua συμβατικά ξεκινούν από τον δείκτη 1. ```lua local t = {"x", "y", "z"} print(t[1]) -- x (Lua: first element is index 1) print(t[3]) -- z print(#t) -- 3 (length) ``` ```perl use v5.42; my @t = ("x", "y", "z"); say $t[0]; # x (Perl: first element is index 0) say $t[2]; # z say $t[-1]; # z (negative indices count from the end) say scalar @t; # 3 (length; @t in scalar context is its size) ``` Οι μεταλλάκτες έχουν μετονομαστεί αλλά είναι οικείοι. Τα `table.insert`/`table.remove` γίνονται `push`/`pop`/`shift`/`unshift`: ```perl use v5.42; my @nums = (10, 20, 30); push @nums, 40; # add to the end my $last = pop @nums; # 40, removed from the end say scalar @nums; # 3 say "@nums[0,1]"; # 10 20 (a slice) ``` Η Lua καταφεύγει σε ρητούς βρόχους `for i = 1, #t do ... end` για να μετασχηματίσει ή να φιλτράρει· η Perl έχει `map` (μετασχηματισμός) και `grep` (φιλτράρισμα), με το `$_` ως το τρέχον στοιχείο: ```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 ``` Η παγίδα: πέρα από την ευρετηρίαση 0-based, το `#t` δεν έχει μία μόνο γραφή στην Perl - χρησιμοποιείτε `scalar @a` για το μήκος ενός πίνακα και `keys %h` για ενός hash. Και το `sort` συγκρίνει ως συμβολοσειρές από προεπιλογή, οπότε το `sort (10, 9, 100)` δίνει `10 100 9`. Για αριθμούς πρέπει να περάσετε έναν συγκριτή: `sort { $a <=> $b } @nums`. Τα `$a` και `$b` είναι τα δύο στοιχεία που συγκρίνονται, και το `<=>` είναι ο αριθμητικός τρίτιμος τελεστής (το `cmp` είναι το αδελφάκι του για συμβολοσειρές). ## Hashes Εδώ είναι το άλλο μισό του διαχωρισμού του table. Ένα table της Lua χρησιμοποιούμενο ως αντιστοίχιση (`{name = "Ada", age = 36}`, ή `t["key"]`) γίνεται ένα `%hash` της Perl. Δηλώνεται με `%`, και προσπελαύνεται μία τιμή τη φορά με `$h{key}`. ```lua local h = {apple = 3, pear = 5, plum = 2} print(h.apple) -- 3 print(h["pear"]) -- 5 h.plum = nil -- delete a key for k, v in pairs(h) do print(k, v) end ``` ```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 } ``` Η παγίδα: η Lua συγχωνεύει τον πίνακα και την αντιστοίχιση σε ένα table και κρατάει και ένα μέρος πίνακα και ένα μέρος hash μέσα του· η Perl τα κρατάει αυστηρά χωριστά, οπότε αποφασίζετε εξαρχής αν ένα πράγμα είναι ένας `@array` (ακέραιες θέσεις, διατεταγμένος) ή ένα `%hash` (κλειδιά συμβολοσειρών, αδιάτακτο). Ο έλεγχος μέλους είναι `exists $h{key}`, το αντίστοιχο του ελέγχου `h[k] ~= nil` της Lua, και το παχύ κόμμα `=>` είναι ο ιδιωματικός διαχωριστής κλειδιού/τιμής που βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του (οπότε το `apple => 3` δεν χρειάζεται εισαγωγικά). Ένα hash δεν έχει σειρά, οπότε επαναλάβετε με `sort keys %h` όταν θέλετε μια σταθερή· το `pairs` της Lua είναι αδιάτακτο για τον ίδιο λόγο. ## Περιβάλλον Αυτό είναι το κεφάλαιο που πρέπει να διαβάσετε δύο φορές. Η Lua δεν έχει γενικό αντίστοιχο, και σχεδόν κάθε ιστορία «η Perl είναι παράξενη» ανάγεται σε αυτό. Αλλά η Lua *όντως* σας δίνει τη μία γέφυρα που το κάνει να βγάζει νόημα: τις **πολλαπλές τιμές επιστροφής**. Στη Lua, το `local a, b = f()` απλώνει τις πολλές τιμές επιστροφής μιας συνάρτησης σε πολλές μεταβλητές, και το `#t` ή το `select("#", ...)` συμπτύσσει μια λίστα σε ένα πλήθος. Η 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`. Η μορφή λίστας `my ($a, $b) = @list` είναι ακριβώς το `local a, b = ...` της Lua - η γέφυρα που ήδη ξέρετε. Μια υπορουτίνα που επιστρέφει πολλές τιμές είναι το ίδιο με μια συνάρτηση της Lua με πολλές τιμές `return`· το περιβάλλον του καλούντος αποφασίζει τι επιστρέφεται: ```perl use v5.42; sub minmax (@nums) { my @s = sort { $a <=> $b } @nums; return ($s[0], $s[-1]); } my ($lo, $hi) = minmax(4, 1, 9, 2); say "$lo $hi"; # 1 9 ``` Μια sub μπορεί ακόμη και να ρωτήσει σε ποιο περιβάλλον κλήθηκε, με `wantarray`, και να απαντήσει διαφορετικά - κάτι που οι συναρτήσεις της Lua δεν μπορούν να κάνουν: ```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 ``` Η παγίδα: δεν υπάρχει μία μόνο απάντηση στο «τι κάνει αυτός ο πίνακας» - εξαρτάται από την περιβάλλουσα έκφραση, εκεί που η Lua επιλύει το άπλωμα πολλαπλών τιμών μόνο στο συγκεκριμένο σημείο κλήσης. Όταν κάτι επιστρέφει έναν αριθμό που περιμένατε να είναι λίστα (ή το αντίστροφο), το περιβάλλον είναι ο πρώτος ύποπτος. Φτάστε στο `scalar` για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων. ## Αλήθεια τιμών Αυτή είναι μια απότομη ρήξη από τη Lua, οπότε επιβραδύνετε. Στη Lua **μόνο τα `nil` και `false` είναι ψευδή** - ο αριθμός `0` και η κενή συμβολοσειρά `""` είναι και τα δύο *αληθή*. Η Perl τραβάει τη γραμμή σε ένα εντελώς διαφορετικό σημείο. Οι ψευδείς τιμές της είναι: ο αριθμός `0`, η κενή συμβολοσειρά `""`, η συμβολοσειρά `"0"`, και το `undef` (το `nil` της Perl). **Όλα τα άλλα είναι αληθή.** ```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 ``` Η παγίδα: το ένστικτο ενός προγραμματιστή της Lua ότι το `0` είναι αληθές θα αντιστρέψει σιωπηλά τη σημασία κάθε `if ($count)`. Στην Perl τα `0` και `""` *είναι* ψευδή, οπότε το `if ($n)` είναι ψευδές όταν το `$n` είναι μηδέν - συνήθως αυτό που θέλετε, αλλά το αντίθετο της Lua. Από την άλλη μεριά, τα `"00"` και `"0.0"` είναι **αληθή** παρόλο που μοιάζουν αριθμητικά μηδέν, επειδή μόνο η ακριβής συμβολοσειρά `"0"` είναι ψευδής. Για μια επιλογή «χρησιμοποίησε αυτό αν είναι ορισμένο, αλλιώς μια προεπιλογή» - το ιδίωμα `x = y or default` της Lua, που σκοντάφτει στα `0` και `false` - χρησιμοποιήστε το defined-or `//` της Perl, που πέφτει πίσω μόνο στο `undef` (το `nil` της Perl): ```perl use v5.42; my %cfg = (host => "localhost"); my $port = $cfg{port} // 8080; # key absent -> undef -> 8080 say $port; # 8080 ``` ## Ροή ελέγχου Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει. Το `elseif` της Lua είναι το `elsif` της Perl, δεν υπάρχει `then`/`do`/`end`, και τα μπλοκ είναι σε άγκιστρα αντί να οριοθετούνται με δεσμευμένες λέξεις. ```lua if n < 0 then sign = "neg" elseif n == 0 then sign = "zero" else sign = "pos" end for _, x in ipairs(nums) do print(x) end while running do step() end ``` ```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 προσθέτει δύο πράγματα που δεν έχει η Lua. Οι **μεταθεματικοί τροποποιητές** επιτρέπουν σε μια απλή εντολή να φέρει τη δική της συνθήκη ή βρόχο, κάτι που διαβάζεται φυσικά για μονόγραμμα: ```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`. Η παγίδα: η Perl δεν έχει εντολή `x if c else y`· η εκφραση υπό συνθήκη είναι ο τριαδικός τελεστής σε στυλ C με `?`/`:`, τον οποίο η Lua συνήθως απομιμείται με `c and x or y`. Η μορφή της Perl είναι άμεση και αλυσιδώνεται: `my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos"`. Ο έλεγχος βρόχου είναι `last`/`next` εκεί που η Lua έχει `break` και (στην 5.4) `goto continue`. ## Υπορουτίνες Το `function` γίνεται `sub`. Η σύγχρονη Perl έχει υπογραφές που ονοματίζουν τις παραμέτρους τους, συμπεριλαμβανομένων των προεπιλογών, αντί για το θεσιακό `...` και τον χειροκίνητο χειρισμό `arg` της Lua. ```lua local function greet(name, greeting) greeting = greeting or "Hello" return greeting .. ", " .. name .. "!" end local function total(...) local sum = 0 for _, n in ipairs({...}) do sum = sum + n end return sum end ``` ```perl 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 ``` Το ιδίωμα ονοματισμένων ορισμάτων της Lua (το πέρασμα ενός μοναδικού table, `f{host=..., port=...}`) αντιστοιχίζεται σε μια παράμετρο 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` (όπως η μόνο-ρητή επιστροφή της Lua, μόνο που η Perl έχει επίσης τη σιωπηρή μορφή της τελευταίας τιμής), και η επιστροφή είναι **ευαίσθητη στο περιβάλλον** (δείτε [Περιβάλλον]()). Αν κάνετε `return @list`, ο καλών παίρνει τη λίστα σε περιβάλλον λίστας αλλά το *πλήθος* σε βαθμωτό περιβάλλον - ένας βαθμός ελευθερίας που η πολλαπλή επιστροφή της Lua δεν έχει. ## Αναφορές Οι πίνακες και τα hashes της Perl δεν εμφωλεύονται απευθείας· τα εμφωλεύετε μέσω **αναφορών**, ενός βαθμωτού που δείχνει σε ένα σύνολο. Στη Lua κάθε table είναι ήδη αναφορά (η ανάθεση ενός table το μοιράζεται, ποτέ δεν το αντιγράφει), οπότε αυτή η έννοια είναι εκεί αόρατη σε εσάς· στην Perl παίρνετε μια αναφορά ρητά με `\`, και την ακολουθείτε με `->`. ```lua local data = {name = "Ada", langs = {"Perl", "Lua"}} print(data.langs[2]) -- Lua table.insert(data.langs, "Ruby") ``` ```perl use v5.42; my $data = { name => "Ada", langs => ["Perl", "Lua"] }; say $data->{name}; # Ada say $data->{langs}[1]; # Lua push $data->{langs}->@*, "Ruby"; # postfix deref to push say "@{$data->{langs}}"; # Perl Lua Ruby ``` Το `{ ... }` φτιάχνει μια ανώνυμη αναφορά hash και το `[ ... ]` μια ανώνυμη αναφορά πίνακα - τα κυριολεκτικά δομικά στοιχεία των εμφωλευμένων δεδομένων. Το βέλος `->` ακολουθεί μια αναφορά κατά ένα βήμα· ανάμεσα σε δύο δείκτες είναι προαιρετικό, οπότε τα `$data->{langs}[1]` και `$data->{langs}->[1]` είναι ίδια. Οι ανώνυμες subs είναι πραγματικά closures, ακριβώς όπως τα closures της Lua πάνω από upvalues - μία από τις πιο καθαρές μεταφορές σε όλον τον οδηγό: ```perl use v5.42; sub counter ($start) { my $n = $start; return sub { return $n++ }; } my $c = counter(10); say $c->(); # 10 say $c->(); # 11 say $c->(); # 12 ``` Η παγίδα: οι μεταθεματικές μορφές αποαναφοράς (`->@*`, `->%*`, `->$*`) είναι ο σύγχρονος, ευανάγνωστος τρόπος να αποαναφέρετε ένα ολόκληρο σύνολο, και το `"@{ ... }"` είναι ο τρόπος να παρεμβάλετε έναν αποαναφερμένο πίνακα μέσα σε μια συμβολοσειρά. Οι παλαιότερες μορφές `@$ref` και `${$ref}{key}` εξακολουθούν να δουλεύουν και εμφανίζονται σε υπάρχοντα κώδικα, αλλά προτιμήστε το βέλος και τις μεταθεματικές μορφές σε νέο κώδικα. ## Κανονικές εκφράσεις Αυτό είναι αναβάθμιση, όχι μετάφραση. Η Lua έχει **μοτίβα** - `string.match`, `string.gsub`, κλάσεις χαρακτήρων όπως το `%d` - τα οποία εσκεμμένα *δεν* είναι κανονικές εκφράσεις και τους λείπει η εναλλαγή, οι ποσοδείκτες, και οι οπισθαναφορές. Η Perl έχει πλήρες regex ως **σύνταξη**: το `=~` συνδέει ένα μοτίβο με μια συμβολοσειρά, το `m//` αντιστοιχίζει, το `s///` αντικαθιστά. Οι ομάδες σύλληψης καταλήγουν στα `$1`, `$2`, και οι ονομαστικές συλλήψεις στο `%+`. ```lua local s = "2026-06-20" local y, m, d = string.match(s, "(%d+)-(%d+)-(%d+)") local t = string.gsub("a,b,c", ",", ";") -- a;b;c ``` ```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` σε περιβάλλον λίστας επιστρέφει *όλες* τις αντιστοιχίες, το άμεσο αντίστοιχο της επανάληψης `string.gmatch`: ```perl use v5.42; my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3 say "@nums"; ``` Η παγίδα: οι μετα-χαρακτήρες διαφέρουν από τα μοτίβα της Lua - η Perl χρησιμοποιεί `\d` εκεί που η Lua χρησιμοποιεί `%d`, ποσοδείκτες `*`/`+`/`?`, εναλλαγή `|`, και ονομαστικές ομάδες `(?...)`, καμία από τις οποίες δεν υποστηρίζουν τα μοτίβα της Lua. Και το `s///` τροποποιεί τον στόχο του *επιτόπια* και επιστρέφει τον αριθμό των αντικαταστάσεων, όχι μια νέα συμβολοσειρά, εκεί που το `gsub` της Lua επιστρέφει μια νέα συμβολοσειρά. Γι” αυτό το παράδειγμα αντιγράφει πρώτα με `(my $t = "a,b,c")` και μετά αντικαθιστά στο αντίγραφο. Για να πάρετε μια νέα συμβολοσειρά χωρίς να αγγίξετε την αρχική, χρησιμοποιήστε τη σημαία `/r`: `my $t = $s =~ s/,/;/gr`. ## I/O αρχείων και ροών Το ιδίωμα είναι το `open` με τρία ορίσματα και ένα λεξιλογικό filehandle, το αντίστοιχο του `io.open` της Lua. Ο τελεστής με γωνιώδεις αγκύλες `<$fh>` διαβάζει μια γραμμή, τον ρόλο του `fh:read("l")` ή του επαναλήπτη `fh:lines()` της Lua. ```lua local f = io.open("/tmp/sample.txt", "w") for i = 1, 3 do f:write("line " .. i .. "\n") end f:close() f = io.open("/tmp/sample.txt", "r") for line in f:lines() do print("got:", line) end f:close() ``` ```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 σε περιβάλλον λίστας: ```perl use v5.42; open my $fh, '<', "/tmp/sample.txt" or die $!; my @lines = <$fh>; # all lines, including newlines say scalar @lines; # 3 ``` Η παγίδα: μια γραμμή που διαβάστηκε με `<$fh>` κρατάει τον τελικό χαρακτήρα νέας γραμμής (το `fh:lines()` της Lua τον αφαιρεί, το `fh:read("L")` τον κρατάει)· καλέστε `chomp` για να τον αφαιρέσετε. Και το `open` επιστρέφει ψευδές σε αποτυχία αντί να εγείρει εξαίρεση, όπως ακριβώς το `io.open` της Lua επιστρέφει `nil, err`, οπότε το ιδίωμα `or die "...: $!"` είναι υποχρεωτικό - το `$!` είναι το μήνυμα σφάλματος συστήματος, το `errno` της Perl. ## Αρθρώματα και πακέτα Το `require` της Lua γίνεται το `use` της Perl. Ο διαχωριστής χώρου ονομάτων είναι το `::`, όχι το `.`, και ένα άρθρωμα `Foo::Bar` βρίσκεται στο `Foo/Bar.pm` στη διαδρομή ένταξης `@INC` (το `package.path` της Lua). Σε αντίθεση με το `require` της Lua, που επιστρέφει ένα table που αναθέτετε εσείς, το `use` εισάγει επιλεγμένα ονόματα απευθείας στον χώρο ονομάτων σας. ```lua local sqrt = math.sqrt local sum = 0 for i = 1, 10 do sum = sum + i end ``` ```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 ``` Εδώ είναι μια ευχάριστη έκπληξη ερχόμενοι από τη σκόπιμα μικροσκοπική τυπική βιβλιοθήκη της Lua: η Perl έρχεται με ένα μεγάλο ενσωματωμένο σύνολο, και πολλά από αυτά που θα έφτιαχνες με το χέρι ή θα τραβούσες από το LuaRocks είναι στον πυρήνα ή ένα `use` μακριά στο CPAN, το αρχείο πακέτων της Perl. Τα `List::Util`, `Scalar::Util`, `POSIX`, `File::Spec`, και `Data::Dumper` είναι τα καθημερινά. Η παγίδα: το `use` τρέχει κατά τη *μεταγλώττιση*, όχι στη γραμμή όπου εμφανίζεται, οπότε η τοποθέτησή του σε σχέση με τον κώδικά σας δεν έχει σημασία όπως έχει μια κλήση `require`. Η εισαγωγή είναι προαιρετική ανά όνομα: το `use List::Util qw(sum0 max)` φέρνει ακριβώς τα `sum0` και `max` στον χώρο ονομάτων σας, τίποτε άλλο. ## Αντικείμενα Η Lua δεν έχει ενσωματωμένο σύστημα αντικειμένων· χτίζετε ένα από tables και metatables - `setmetatable(t, {__index = Class})` για αναζήτηση μεθόδου και κληρονομικότητα, `__add`/`__eq` για τελεστές. Η Perl 5.42 σας δίνει αντ” αυτού μια σύνταξη `class` πρώτης τάξης, οπότε η συνηθισμένη περίπτωση δεν χρειάζεται καλωδίωση metatable. Τα πεδία δηλώνονται με `field`, οι μέθοδοι με `method`, και το `$self` είναι διαθέσιμο μέσα σε κάθε μέθοδο (δεν το περνάτε ως την πρώτη παράμετρο όπως κάνει το `self` / η κλήση με άνω-κάτω-τελεία της Lua). ```lua local Point = {} Point.__index = Point function Point.new(x, y) return setmetatable({x = x or 0, y = y or 0}, Point) end function Point:to_string() return "(" .. self.x .. ", " .. self.y .. ")" end function Point:move(dx, dy) self.x = self.x + dx self.y = self.y + dy return self end local p = Point.new(3, 4) print(p:to_string()) -- (3, 4) p:move(1, 1) print(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) ``` Ο κατασκευαστής δημιουργείται για εσάς, αντικαθιστώντας το γραμμένο-με-το-χέρι `Point.new` της Lua: το `:param` σημειώνει ένα πεδίο που η αυτόματα κατασκευασμένη `new` δέχεται ως ονοματισμένο όρισμα, και το `= 0` είναι η προεπιλογή του. Η κληρονομικότητα, την οποία η Lua χτίζει από μια αλυσίδα metatables `__index`, είναι το attribute `:isa`: ```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** ακόμη και όταν προσπαθείτε να τις σιγάσετε. Δεν επηρεάζουν την έξοδο του προγράμματός σας, οπότε ένα σενάριο που τυπώνει στο stdout συμπεριφέρεται ακριβώς όπως φαίνεται παραπάνω· απλώς αναμένετε τις ειδοποιήσεις στη ροή σφαλμάτων. Οι μεταμέθοδοι τελεστών της Lua (`__add`, `__eq`, και παρόμοιες) αντιστοιχίζονται στην pragma `overload` της Perl παρά στο `class`· γι” αυτό, για το παλαιότερο μοντέλο αντικειμένων βασισμένο στο `bless`, για ρόλους, και για λεπτομέρειες κληρονομικότητας, δείτε τον [οδηγό Αντικειμενοστραφούς Προγραμματισμού](../oop/index.md). ## Χειρισμός σφαλμάτων Η προστατευμένη κλήση `pcall(f)` της Lua - που επιστρέφει `false, err` αντί να ξετυλίγει - έχει δύο αντίστοιχα στην Perl. Το κλασικό τυλίγει κώδικα σε `eval { }` και εξετάζει το `$@` (το σφάλμα που πιάστηκε) μετά: ```lua local ok, err = pcall(function() risky(-1) end) if not ok then print("caught: " .. err) end ``` ```perl use v5.42; my $result = eval { risky(-1) }; if ($@) { print "caught: $@"; # caught: negative! } ``` Η σύγχρονη μορφή είναι το `try`/`catch`, που διαβάζεται πιο πολύ σαν ένα συμβατικό μπλοκ εξαίρεσης παρά σαν τον χορό λογικής-επιστροφής του `pcall`: ```perl use v5.42; use feature 'try'; try { risky(-4); } catch ($e) { print "caught: $e"; # caught: negative! } ``` όπου η πλευρά που εγείρει είναι το `die`, το `error` της Perl: ```perl use v5.42; sub risky ($n) { die "negative!\n" if $n < 0; return sqrt $n; } ``` Η παγίδα: το `die` με μια συμβολοσειρά που τελειώνει σε `\n` παράγει ακριβώς αυτό το μήνυμα· το `die` με μια συμβολοσειρά που *δεν* τελειώνει σε χαρακτήρα νέας γραμμής παίρνει αυτόματα την προσθήκη `" at SCRIPT line N.\n"` (το `error` της Lua κάνει παρόμοιο προθεματισμό θέσης εκτός αν περάσετε επίπεδο `0`). Τελειώστε τις συμβολοσειρές σφάλματός σας με `\n` όταν θέλετε ένα καθαρό μήνυμα και παραλείψτε το όταν θέλετε το ίχνος αρχείου-και-γραμμής. Σημειώστε επίσης τις δύο μεταβλητές σφάλματος: το `$@` είναι η τελευταία εξαίρεση που πιάστηκε, ενώ το `$!` είναι το τελευταίο σφάλμα *συστήματος* (ένα αποτυχημένο `open`, η συμβολοσειρά `errno`). ## Ιδιωματισμοί Μερικά μοτίβα που θα βλέπετε καθημερινά και θα θέλετε στα δάχτυλά σας. Ταξινομήστε μια λίστα με βάση ένα υπολογισμένο κλειδί (ο Schwartzian transform - διακόσμησε, ταξινόμησε, αφαίρεσε τη διακόσμηση σε μία σωλήνωση, το αντανακλαστικό της Perl εκεί που η Lua περνάει μια συνάρτηση συγκριτή στο `table.sort`): ```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 από μια λίστα, μετατρέποντας μια ακολουθία σε πίνακα αναζήτησης σε ένα βήμα: ```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`, έναν ρόλο που η Lua δεν στοχεύει. Το μονόγραμμο με σιωπηρό βρόχο είναι το καθημερινό αντανακλαστικό του προγραμματιστή της Perl: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` Η παγίδα: διαβάστε τον [οδηγό Μονόγραμμων](../oneliner/index.md) για τους διακόπτες (`-n`, `-p`, `-a`, `-l`, `-e`) και τις δεκάδες συνταγές που είναι χτισμένες πάνω τους· είναι ένα από τα πράγματα με τη μεγαλύτερη μόχλευση που μπορείτε να μάθετε ερχόμενοι από υπόβαθρο scripting όπου αυτή η εξειδικευμένη θέση ήταν δουλειά κάποιου άλλου. ## Παγίδες ερχόμενοι από τη Lua Οι παγίδες, συγκεντρωμένες για μια τελική σάρωση πριν αρχίσετε να γράφετε: - **Ένα table γίνεται δύο τύποι.** Ένα table της Lua είναι ένας `@array` (με ακέραια κλειδιά, διατεταγμένος) *ή* ένα `%hash` (με κλειδιά συμβολοσειρών, αδιάτακτο) στην Perl, ποτέ και τα δύο μαζί. Αποφασίστε ποιο έχετε πριν το χρησιμοποιήσετε. - **Οι πίνακες είναι 0-based.** Το `t[1]` της Lua είναι το `$t[0]` της Perl· το τελευταίο στοιχείο είναι `$t[-1]`. Αυτό είναι το πιο συχνό σφάλμα ολίσθησης-κατά-ένα κατά τη μεταφορά. - **Τα sigils αλλάζουν με τη χρήση.** Ένα στοιχείο του `@a` είναι `$a[0]`· μία τιμή του `%h` είναι `$h{k}`. Το sigil του περιέκτη και το sigil του στοιχείου διαφέρουν - το σκέτο όνομα `t` της Lua δεν έχει τέτοια μετατόπιση. - **Η συνένωση είναι `.`, όχι `..`.** Το `..` της Lua δεν υπάρχει· το `.` ενώνει συμβολοσειρές και το `x` τις επαναλαμβάνει. - **Η αλήθεια είναι αντεστραμμένη στο μηδέν.** Στη Lua τα `0` και `""` είναι *αληθή*· στην Perl είναι **ψευδή**. Μόνο τα `0`, `""`, `"0"`, και `undef` είναι ψευδή, οπότε τα `"00"` και `"0.0"` είναι παραδόξως **αληθή**. - **Δύο οικογένειες σύγκρισης.** `==`/`<`/`<=>` για αριθμούς, `eq`/`lt`/`cmp` για συμβολοσειρές. Επιλέξτε με βάση τον τύπο του τελεστέου· το μοναδικό `==` της Lua δεν έχει αντίστοιχο διαχωρισμό. - **Το `sort` ταξινομεί ως συμβολοσειρές από προεπιλογή.** Περάστε `{ $a <=> $b }` για αριθμητική σειρά. - **Περιβάλλον.** Το `my $x = @a` είναι το *μήκος*· το `my ($x) = @a` είναι το *πρώτο στοιχείο*. Η μορφή λίστας είναι ο φίλος σας `local a, b = f()`· η βαθμωτή μορφή είναι καινούργια. Ξαναδιαβάστε το [Περιβάλλον]() όταν μια τιμή επιστρέφει με λάθος σχήμα. - **Το regex δεν είναι τα μοτίβα της Lua.** Η Perl έχει πλήρες regex (`\d`, `|`, `*`, ονομαστικές ομάδες)· τα μοτίβα της Lua (`%d`, χωρίς εναλλαγή) είναι μια διαφορετική, μικρότερη γλώσσα. Το `s///` επεξεργάζεται επιτόπια και επιστρέφει ένα πλήθος· χρησιμοποιήστε `/r` για μια νέα συμβολοσειρά. - **Το `open` δεν εγείρει εξαίρεση.** Χρησιμοποιήστε `open ... or die "...: $!"`, όπως το `io.open` της Lua επιστρέφει `nil, err`. - **Κάντε `chomp` στις αναγνώσεις γραμμών σας.** Το `<$fh>` κρατάει τον τελικό χαρακτήρα νέας γραμμής που το `lines()` της Lua θα είχε αφαιρέσει. - **Η μη-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. Operator metamethods map to the `overload` pragma, not to `class`.