# Από 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: ```perl use v5.42; ``` Αυτή η μία γραμμή ενεργοποιεί το `strict` (καμία τυχαία καθολική μεταβλητή), τα `warnings`, την ενσωματωμένη `say` (ένα `print` με τελικό χαρακτήρα νέας γραμμής, όπως το `print` της Python), τις υπογραφές υπορουτινών, και το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται σε όλον αυτόν τον οδηγό. ## Πώς να το διαβάσετε αυτό - [Μεταβλητές και sigils]() - η μετάβαση `$`/`@`/`%`. - [Συμβολοσειρές]() - παρεμβολή και συνένωση. - [Αριθμοί]() - ένας επιφανειακός τύπος, διαίρεση κινητής υποδιαστολής. - [Πίνακες και λίστες]() - η `list` σας, συν `map`/`grep`. - [Hashes]() - το `dict` σας. - [Περιβάλλον]() - η έννοια για την οποία η Python δεν έχει λέξη. Διαβάστε αυτήν. - [Αλήθεια]() - τι μετράει ως ψευδές, με εκπλήξεις. - [Ροή ελέγχου]() - `if`, βρόχοι, οι μεταθεματικές μορφές. - [Υπορουτίνες]() - το `def` γίνεται `sub`. - [Αναφορές]() - εμφωλευμένα δεδομένα και closures. - [Κανονικές εκφράσεις]() - το regex ως σύνταξη, όχι ως βιβλιοθήκη. - [I/O αρχείων και ροών]() - `open`, αναγνώσεις γραμμών, slurp. - [Αρθρώματα και πακέτα]() - το `import` γίνεται `use`. - [Αντικείμενα]() - η σύγχρονη σύνταξη `class`. - [Χειρισμός σφαλμάτων]() - το `try`/`except` γίνεται `eval` ή `try`/`catch`. - [Ιδιώματα]() - τα καθημερινά μονόγραμμα. - [Παγίδες ερχόμενοι από την Python]() - σαρώστε τες πριν γράψετε. ## Μεταβλητές και sigils Στην Python ένα όνομα απλώς ονοματίζει μια τιμή. Στην Perl το **sigil σας λέει το σχήμα**: `$` για μία τιμή, `@` για μια διατεταγμένη λίστα, `%` για μια αντιστοίχιση κλειδιού/τιμής. Το `my` δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, το αντίστοιχο της Perl για μια συνηθισμένη τοπική σύνδεση. ```python name = "Ada" langs = ["Python", "Perl"] ages = {"Ada": 36, "Linus": 56} ``` ```perl 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. Η συνένωση είναι `.`, όχι `+` (το οποίο προορίζεται για αριθμούς). ```python name = "Ada" greeting = f"Hello, {name}" shout = "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`, όχι `*`, και ο τελεστής ένωσης είναι `.`, όχι `+`. Το μπέρδεμά τους είναι το πιο συχνό τυπογραφικό λάθος της πρώτης μέρας για έναν προγραμματιστή της Python. Τα στοιχεία πίνακα και οι τιμές hash παρεμβάλλονται και μέσα σε διπλά εισαγωγικά: το `"First: $langs[0]"` δουλεύει όπως είναι γραμμένο. ## Αριθμοί Η Perl έχει έναν αριθμητικό τύπο στην επιφάνεια· δεν δηλώνετε `int` έναντι `float`. Η διαίρεση πάντα παράγει έναν αριθμό κινητής υποδιαστολής, ακριβώς όπως η Python 3. ```python print(7 / 2) # 3.5 print(7 // 2) # 3 print(2 ** 10) # 1024 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"` είναι ψευδές (συμβολοσειρά). Η χρήση `==` σε συμβολοσειρές, ή `eq` σε αριθμούς, είναι ένα συχνό και σιωπηλό σφάλμα. ## Πίνακες και λίστες Ένας πίνακας της Perl είναι η `list` σας στην Python. Τα ρήματα έχουν μετονομαστεί αλλά οι λειτουργίες είναι ίδιες, και οι αρνητικοί δείκτες δουλεύουν όπως περιμένετε. ```python nums = [1, 2, 3, 4, 5] nums.append(6) last = nums.pop() print(len(nums)) # 5 print(nums[1:3]) # [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) ``` Τα comprehensions γίνονται `map` και `grep`. Το `map` μετασχηματίζει (το `[f(x) for x in xs]` της Python)· το `grep` φιλτράρει (το `[x for x in xs if p(x)]` της Python). Το `$_` είναι το τρέχον στοιχείο, η σιωπηρή μεταβλητή βρόχου της Perl. ```python squares = [x * x for x in nums] evens = [x for x in nums if x % 2 == 0] desc = sorted(nums, reverse=True) ``` ```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` είναι το αδελφάκι του για συμβολοσειρές). ## Hashes Ένα hash της Perl είναι το `dict` σας στην Python. Δηλώνεται με `%`, και προσπελαύνεται μία τιμή τη φορά με `$h{key}`. ```python 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]) ``` ```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}"; } ``` Η παγίδα: ο έλεγχος μέλους είναι `exists $h{key}`, όχι το `in` της Python, και το παχύ κόμμα `=>` είναι ο ιδιωματικός διαχωριστής κλειδιού/τιμής. Το `=>` επίσης βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του, οπότε το `apple => 3` δεν χρειάζεται εισαγωγικά γύρω από το `apple`. Ένα hash δεν έχει σειρά· επαναλάβετε με `sort keys %h` όταν θέλετε μια σταθερή. ## Περιβάλλον Αυτό είναι το κεφάλαιο που πρέπει να διαβάσετε δύο φορές. Η Python δεν έχει αντίστοιχο, και σχεδόν κάθε ιστορία «η 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 ($a, $b) = @list; # 10 and 20 say "$a $b"; # 10 20 ``` Οι παρενθέσεις στα αριστερά είναι όλη η διαφορά. Το `my $x = @list` ρωτάει «πόσα;» και παίρνει `3`. Το `my ($x) = @list` ρωτάει «δώσε μου τα στοιχεία» και παίρνει `10`. Ένας προγραμματιστής της Python διαβάζει και τα δύο ως «ανάθεσε τη λίστα», που είναι ακριβώς η παγίδα. Μια υπορουτίνα μπορεί να ρωτήσει σε ποιο περιβάλλον κλήθηκε, με `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` επιβάλλει βαθμωτό περιβάλλον για να πάρει ένα πλήθος· μια αντιστοίχιση regex με `/g` επιστρέφει τη λίστα των αντιστοιχιών σε περιβάλλον λίστας και ένα αληθές/ψευδές σε βαθμωτό περιβάλλον. Το κλασικό ιδίωμα για το «μέτρα τις αντιστοιχίες» επιβάλλει μια ανάθεση λίστας σε βαθμωτό περιβάλλον με τη λεγόμενη μορφή *countof*: ```perl use v5.42; my $n = () = "a1b2c3" =~ /\d/g; say $n; # 3 ``` Η παγίδα: δεν υπάρχει μία μόνο απάντηση στο «τι κάνει αυτός ο πίνακας» - εξαρτάται από την περιβάλλουσα έκφραση. Όταν κάτι επιστρέφει έναν αριθμό που περιμένατε να είναι λίστα (ή το αντίστροφο), το περιβάλλον είναι ο πρώτος ύποπτος. Φτάστε στο `scalar` για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων. ## Αλήθεια τιμών Οι ψευδείς τιμές της Perl είναι μια σύντομη, συγκεκριμένη λίστα: ο αριθμός `0`, η κενή συμβολοσειρά `""`, η συμβολοσειρά `"0"`, και το `undef` (το `None` της Perl). **Όλα τα άλλα είναι αληθή** - και αυτό περιλαμβάνει κάποιες συμβολοσειρές που ένας προγραμματιστής της Python υποθέτει ότι είναι ψευδείς. ```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, παρόλο που μοιάζουν αριθμητικά μηδέν, επειδή μόνο η ακριβής συμβολοσειρά `"0"` είναι ψευδής. Η συμβολοσειρά `"false"` είναι αληθής (είναι μια μη κενή, μη-`"0"` συμβολοσειρά). Και σε αντίθεση με την Python, ένας κενός πίνακας ή κενό hash δεν είναι ο ίδιος μια ψευδής *τιμή* - σε λογικό περιβάλλον ένας πίνακας δίνει το μήκος του, οπότε το `if (@list)` είναι αληθές όταν η λίστα είναι μη κενή, που είναι ο έλεγχος που θέλετε. Για μια επιλογή «χρησιμοποίησε αυτό αν είναι ορισμένο, αλλιώς μια προεπιλογή», χρησιμοποιήστε το defined-or `//`, που πέφτει πίσω μόνο στο `undef` (όχι στο `0` ή στο `""`): ```perl use v5.42; my %cfg = (host => "localhost"); my $port = $cfg{port} // 8080; # key absent -> undef -> 8080 say $port; # 8080 ``` ## Ροή ελέγχου Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει. Το `elif` είναι `elsif`, και δεν υπάρχει `:` ούτε εσοχή - τα μπλοκ είναι σε άγκιστρα. ```python if n < 0: sign = "neg" elif n == 0: sign = "zero" else: sign = "pos" for x in nums: print(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 προσθέτει δύο πράγματα που δεν έχει η Python. Οι **μεταθεματικοί τροποποιητές** επιτρέπουν σε μια απλή εντολή να φέρει τη δική της συνθήκη ή βρόχο, κάτι που διαβάζεται φυσικά για μονόγραμμα: ```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`. Η παγίδα: ο τριαδικός τελεστής αλυσιδώνεται όπως της Python, αλλά με `?`/`:`, όχι `if`/`else`: `my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos"`. Το `?:` σε στυλ C είναι το ιδίωμα· δεν υπάρχει μορφή `x if c else y`. ## Υπορουτίνες Το `def` γίνεται `sub`. Η σύγχρονη Perl έχει υπογραφές που μοιάζουν πολύ με τις λίστες παραμέτρων της Python, συμπεριλαμβανομένων των προεπιλογών. ```python def greet(name, greeting="Hello"): return f"{greeting}, {name}!" def total(*nums): return sum(nums) ``` ```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 ``` Τα ονοματισμένα ορίσματα αντιστοιχίζονται σε μια παράμετρο 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`, ο καλών παίρνει τη λίστα σε περιβάλλον λίστας αλλά το *πλήθος* σε βαθμωτό περιβάλλον. Να είστε εσκεμμένοι σχετικά με το τι επιστρέφετε όταν μπορεί να χρησιμοποιηθεί με οποιονδήποτε τρόπο. ## Αναφορές Οι πίνακες και τα hashes της Perl δεν εμφωλεύονται απευθείας· τα εμφωλεύετε μέσω **αναφορών**, που είναι το αντίστοιχο της Perl για τις λαβές αντικειμένων της Python (στην Python κάθε περιέκτης είναι ήδη αναφορά, οπότε αυτό το βήμα είναι εκεί αόρατο σε εσάς). Μια αναφορά είναι ένα βαθμωτό που δείχνει σε ένα σύνολο. Πάρτε μία με `\`, ακολουθήστε τη με `->`. ```python data = {"name": "Ada", "langs": ["Perl", "Python"]} print(data["langs"][1]) # Python data["langs"].append("Ruby") ``` ```perl 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 αλλά χωρίς τον περιορισμό της μίας έκφρασης: ```perl use v5.42; my $double = sub ($n) { $n * 2 }; say $double->(21); # 42 ``` Η παγίδα: οι μεταθεματικές μορφές αποαναφοράς (`->@*`, `->%*`, `->$*`) είναι ο σύγχρονος, ευανάγνωστος τρόπος να αποαναφέρετε ένα ολόκληρο σύνολο, και το `"@{ ... }"` είναι ο τρόπος να παρεμβάλετε έναν αποαναφερμένο πίνακα μέσα σε μια συμβολοσειρά. Οι παλαιότερες μορφές `@$ref` και `${$ref}{key}` εξακολουθούν να δουλεύουν και εμφανίζονται σε υπάρχοντα κώδικα, αλλά προτιμήστε το βέλος και τις μεταθεματικές μορφές σε νέο κώδικα. ## Κανονικές εκφράσεις Στην Python το regex είναι η βιβλιοθήκη `re`. Στην Perl είναι **σύνταξη**: το `=~` συνδέει ένα μοτίβο με μια συμβολοσειρά, το `m//` αντιστοιχίζει, το `s///` αντικαθιστά. Οι ομάδες σύλληψης καταλήγουν στα `$1`, `$2`, και οι ονομαστικές συλλήψεις στο `%+`. ```python 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(",") ``` ```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` σε περιβάλλον λίστας επιστρέφει *όλες* τις συλλήψεις, που είναι το άμεσο αντίστοιχο του `re.findall`: ```perl 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. ```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()) ``` ```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>` κρατάει τον τελικό χαρακτήρα νέας γραμμής· καλέστε `chomp` για να τον αφαιρέσετε (το αντανακλαστικό `line.rstrip()` της Python). Και το `open` επιστρέφει ψευδές σε αποτυχία αντί να εγείρει εξαίρεση, οπότε το ιδίωμα `or die "...: $!"` είναι υποχρεωτικό - το `$!` είναι το μήνυμα σφάλματος συστήματος, το `errno` της Perl. ## Αρθρώματα και πακέτα Το `import` γίνεται `use`. Ο διαχωριστής χώρου ονομάτων είναι το `::`, όχι το `.`, και ένα άρθρωμα `Foo::Bar` βρίσκεται στο `Foo/Bar.pm` στη διαδρομή ένταξης `@INC` (το `sys.path` της Perl). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων, όπως ακριβώς το `from module import name`. ```python from math import sqrt import json data = json.loads(text) ``` ```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 ``` Η τυπική βιβλιοθήκη είναι μεγάλη και πολλά από αυτά για τα οποία θα κατέφευγες στο 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 (δεν το αναφέρετε ως παράμετρο). ```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) ``` ```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) ``` Ο κατασκευαστής δημιουργείται για εσάς: το `:param` σημειώνει ένα πεδίο που η αυτόματα κατασκευασμένη `new` δέχεται ως ονοματισμένο όρισμα, και το `= 0` είναι η προεπιλογή του. Η κληρονομικότητα είναι `: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** ακόμη και με `no warnings 'experimental::class'` εντός εμβέλειας. Δεν επηρεάζουν την έξοδο του προγράμματός σας, οπότε ένα σενάριο που τυπώνει στο stdout συμπεριφέρεται ακριβώς όπως φαίνεται παραπάνω· απλώς αναμένετε τις ειδοποιήσεις στη ροή σφαλμάτων. Για το παλαιότερο μοντέλο αντικειμένων βασισμένο στο `bless` που θα συναντήσετε σε υπάρχοντα κώδικα, και για ρόλους, λεπτομέρειες κληρονομικότητας, και μετάβαση, δείτε τον [οδηγό Αντικειμενοστραφούς Προγραμματισμού](../oop/index.md). ## Χειρισμός σφαλμάτων Το `try`/`except` της Python έχει δύο αντίστοιχα. Το κλασικό τυλίγει κώδικα σε `eval { }` και εξετάζει το `$@` (το σφάλμα που πιάστηκε) μετά: ```python try: risky(-1) except Exception as e: print("caught:", e) ``` ```perl use v5.42; my $result = eval { risky(-1) }; if ($@) { print "caught: $@"; # caught: negative! } ``` Η σύγχρονη μορφή είναι το `try`/`catch`, που διαβάζεται σχεδόν ακριβώς όπως της Python: ```perl use v5.42; use feature 'try'; try { risky(-4); } catch ($e) { print "caught: $e"; # caught: negative! } ``` όπου η πλευρά που εγείρει είναι το `die`: ```perl 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 γινόμενο με το χέρι, διακόσμησε μετά ταξινόμησε μετά αφαίρεσε τη διακόσμηση): ```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 από μια λίστα, το αντίστοιχο του dict-comprehension (`{w: len(w) for w in words}`): ```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`. Το μονόγραμμο με σιωπηρό βρόχο είναι το καθημερινό αντανακλαστικό του προγραμματιστή της Perl: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` Η παγίδα: διαβάστε τον [οδηγό Μονόγραμμων](../oneliner/index.md) για τους διακόπτες (`-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.