# Από Ruby σε Perl Ξέρετε Ruby. Περνάτε blocks στα `each` και `map` χωρίς να το σκέφτεστε, φτάνετε σε ένα `Hash` αντανακλαστικά, και το `arr.select { |x| ... }` είναι μυϊκή μνήμη. Αυτός ο οδηγός αντιστοιχίζει καθεμία από αυτές τις συνήθειες στην Perl, ώστε να γίνετε παραγωγικοί στο `pperl` μέσα σε ένα απόγευμα αντί να ξαναμάθετε προγραμματισμό από το μηδέν. Δύο μετατοπίσεις φτάνουν μέσα στα πρώτα πέντε λεπτά, οπότε γνωρίστε τες τώρα: - **Τα sigils σημειώνουν τον τύπο, όχι την εμβέλεια.** Η Ruby χρησιμοποιεί `@var` για κατάσταση στιγμιότυπου, `$var` για καθολικές, και ένα σκέτο `var` για τοπικές. Η Perl χρησιμοποιεί `$x` για μία τιμή, `@x` για μια διατεταγμένη λίστα, `%x` για μια αντιστοίχιση κλειδιού/τιμής - το sigil σας λέει το *σχήμα* των δεδομένων, και είναι το ίδιο είτε η μεταβλητή είναι τοπική είτε καθολική. Η πρώτη ενότητα δεν αφορά τίποτε άλλο. - **Δεν είναι τα πάντα αντικείμενο.** Στη Ruby τα `"foo".upcase` και `[1, 2].sum` είναι κλήσεις μεθόδων επειδή οι αριθμοί, οι συμβολοσειρές και οι πίνακες είναι όλα αντικείμενα. Η Perl είναι πρακτική: ένα βαθμωτό δεν είναι αντικείμενο, δεν υπάρχει καθολική βασική κλάση, και αυτές οι λειτουργίες είναι απλές συναρτήσεις - `uc "foo"`, `sum @a`. Καλείτε μεθόδους μόνο σε πράγματα που κάνατε bless σε μια κλάση. 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` με τελικό χαρακτήρα νέας γραμμής, όπως το `puts` της Ruby), τις υπογραφές υπορουτινών, και το σύγχρονο σύνολο χαρακτηριστικών που χρησιμοποιείται σε όλον αυτόν τον οδηγό. ## Πώς να το διαβάσετε αυτό - [Μεταβλητές και sigils]() - η μετάβαση `$`/`@`/`%`. - [Συμβολοσειρές]() - παρεμβολή και συνένωση. - [Αριθμοί]() - ένας επιφανειακός τύπος, διαίρεση κινητής υποδιαστολής. - [Πίνακες και λίστες]() - ο `Array` σας, συν `map`/`grep` που δέχονται block. - [Hashes]() - το `Hash` σας, και πού πάνε τα σύμβολα. - [Περιβάλλον]() - η έννοια για την οποία η Ruby δεν έχει λέξη. Διαβάστε αυτήν. - [Αλήθεια]() - τι μετράει ως ψευδές, και διαφέρει έντονα. - [Ροή ελέγχου]() - `if`, βρόχοι, οι μεταθεματικές μορφές. - [Υπορουτίνες]() - το `def` γίνεται `sub`. - [Αναφορές]() - εμφωλευμένα δεδομένα, και blocks ως τιμές. - [Κανονικές εκφράσεις]() - το regex ως σύνταξη, όπως της Ruby. - [I/O αρχείων και ροών]() - `open`, αναγνώσεις γραμμών, slurp. - [Αρθρώματα και πακέτα]() - το `require` γίνεται `use`. - [Αντικείμενα]() - η σύγχρονη σύνταξη `class`. - [Χειρισμός σφαλμάτων]() - το `begin`/`rescue` γίνεται `eval` ή `try`/`catch`. - [Ιδιώματα]() - τα καθημερινά μονόγραμμα. - [Παγίδες ερχόμενοι από τη Ruby]() - σαρώστε τες πριν γράψετε. ## Μεταβλητές και sigils Στη Ruby ένα sigil σας λέει την *εμβέλεια*: το `@x` είναι μεταβλητή στιγμιότυπου, το `$x` καθολική, το `x` τοπική. Στην Perl το **sigil σας λέει το σχήμα**: `$` για μία τιμή, `@` για μια διατεταγμένη λίστα, `%` για μια αντιστοίχιση κλειδιού/τιμής. Το `my` δηλώνει μια μεταβλητή με λεξιλογική εμβέλεια, το αντίστοιχο της Perl για μια συνηθισμένη τοπική σύνδεση - είναι ο τρόπος που παίρνετε μια τοπική στην Perl, εκεί που η Ruby απλώς χρησιμοποιεί ένα σκέτο όνομα. ```ruby name = "Ada" langs = ["Ruby", "Perl"] ages = {"Ada" => 36, "Yukihiro" => 60} ``` ```perl use v5.42; my $name = "Ada"; my @langs = ("Ruby", "Perl"); my %ages = (Ada => 36, Yukihiro => 60); say $name; # Ada say $langs[0]; # Ruby say $ages{Ada}; # 36 ``` Η παγίδα: όταν φτάνετε *μέσα* σε ένα σύνολο για ένα στοιχείο, το sigil ακολουθεί την τιμή που παίρνετε πίσω, όχι τον περιέκτη. Ένα μεμονωμένο στοιχείο του `@langs` είναι βαθμωτό, οπότε είναι `$langs[0]`, όχι `@langs[0]`· μία τιμή από το `%ages` είναι `$ages{Ada}`. Η μορφή `@`/`%` είναι όλη η συλλογή· η μορφή `$` είναι ένα κομμάτι της. (Και ξεχάστε την αντιστοίχιση της Ruby: ένα `$x` της Perl δεν είναι ποτέ «καθολική» όπως είναι το `$x` της Ruby - το `$` εδώ σημαίνει απλώς «μία τιμή».) ## Συμβολοσειρές Τα διπλά εισαγωγικά παρεμβάλλουν μεταβλητές, τα μονά όχι, ακριβώς όπως τα `"..."` έναντι `'...'` της Ruby. Η μορφή `#{expr}` της Ruby έχει ένα άμεσο αντίστοιχο για σκέτες μεταβλητές και ένα ελαφρώς μακρύτερο για αυθαίρετες εκφράσεις. Η συνένωση είναι `.`, όχι `+`. ```ruby name = "Ada" greeting = "Hello, #{name}" total = "Total: #{6 * 7}" 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 $total = "Total: @{[ 6 * 7 ]}"; # Total: 42 my $joined = "AB" . "CD"; # ABCD my $shout = "AB" x 3; # ABABAB ``` Η παγίδα: ένα σκέτο `"$name"` παρεμβάλλει μια μεταβλητή ακριβώς όπως το `#{name}` της Ruby, αλλά για να παρεμβάλετε μια *έκφραση* την τυλίγετε στη μορφή `@{[ ... ]}` (φτιάχνει έναν πρόσκαιρο πίνακα και τον ενσωματώνει). Η επανάληψη συμβολοσειράς είναι `x`, όχι `*`, και ο τελεστής ένωσης είναι `.`, όχι `+`· το `+` σε συμβολοσειρές τις εξαναγκάζει σε αριθμούς και προειδοποιεί. Τα στοιχεία πίνακα και οι τιμές hash παρεμβάλλονται και μέσα σε διπλά εισαγωγικά: το `"First: $langs[0]"` δουλεύει όπως είναι γραμμένο. ## Αριθμοί Η Perl έχει έναν αριθμητικό τύπο στην επιφάνεια· δεν ξεχωρίζετε `Integer` από `Float`. Η διαίρεση πάντα παράγει έναν αριθμό κινητής υποδιαστολής, όπως η διαίρεση `Float` της Ruby όταν κάποιος από τους τελεστέους είναι float. ```ruby puts 7.0 / 2 # 3.5 puts 7 / 2 # 3 (integer division in Ruby) puts 2 ** 10 # 1024 puts 10 % 3 # 1 ``` ```perl use v5.42; say 7 / 2; # 3.5 (always float) say int(7 / 2); # 3 say 2 ** 10; # 1024 say 10 % 3; # 1 ``` Η παγίδα: οι τελεστές σύγκρισης και ισότητας έρχονται σε δύο οικογένειες, και επιλέγετε με βάση τον *τύπο των τελεστέων*, όχι των μεταβλητών. Οι αριθμοί χρησιμοποιούν `==`, `!=`, `<`, `>`, `<=>`· οι συμβολοσειρές χρησιμοποιούν `eq`, `ne`, `lt`, `gt`, `cmp`. Αυτό δεν έχει αντίστοιχο στη Ruby - το `==` της Ruby αποστέλλει με βάση την κλάση του αντικειμένου, ενώ το `==` της Perl σημαίνει πάντα *αριθμητική* ισότητα. Το `"10" == "10.0"` είναι αληθές (αριθμητικά), αλλά το `"10" eq "10.0"` είναι ψευδές (συμβολοσειρά). Η χρήση `==` σε συμβολοσειρές, ή `eq` σε αριθμούς, είναι ένα συχνό και σιωπηλό σφάλμα. ## Πίνακες και λίστες Ένας πίνακας της Perl είναι ο `Array` σας στη Ruby. Η μεγάλη μετατόπιση είναι ότι οι κλήσεις μεθόδων γίνονται συναρτήσεις: τα `arr.push(x)`, `arr.pop`, `arr.length` της Ruby γίνονται `push @arr, x`, `pop @arr`, `scalar @arr` - ο πίνακας είναι όρισμα, όχι αποδέκτης. ```ruby nums = [1, 2, 3, 4, 5] nums.push(6) last = nums.pop # 6 puts nums.length # 5 puts nums[1, 2].join(" ") # 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) ``` Οι επαναλήπτες της Ruby που δέχονται block είναι το πλουσιότερο μέρος αυτής της αντιστοίχισης. Τα `map`, `select`, και `sort_by` γίνονται `map`, `grep`, και `sort` της Perl, που δέχονται ένα block `{ ... }` ακριβώς όπως οι μέθοδοι της Ruby. Μέσα στο block, το `$_` είναι το τρέχον στοιχείο - η σιωπηρή μεταβλητή block της Perl, ο ρόλος που παίζει η παράμετρος `|x|` της Ruby. ```ruby squares = nums.map { |x| x * x } evens = nums.select { |x| x.even? } desc = nums.sort { |a, b| b <=> a } ``` ```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` είναι τα δύο στοιχεία που συγκρίνονται (το αντίστοιχο του `|a, b|` της Ruby), και το `<=>` είναι ο αριθμητικός τρίτιμος τελεστής - το ίδιο ακριβώς διαστημόπλοιο που ξέρετε από τη Ruby (το `cmp` είναι το αδελφάκι του για συμβολοσειρές). ## Hashes Ένα hash της Perl είναι το `Hash` σας στη Ruby. Δηλώνεται με `%`, και προσπελαύνεται μία τιμή τη φορά με `$h{key}`. Το παχύ κόμμα `=>` που ήδη χρησιμοποιείτε στις κυριολεξίες hash της Ruby δουλεύει κι εδώ, και βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του. ```ruby h = {apple: 3, pear: 5, plum: 2} puts h[:apple] # 3 puts h.keys.sort.join(",") # apple,pear,plum puts h.key?(:pear) # true h.delete(:plum) h.sort.each { |k, v| puts "#{k} => #{v}" } ``` ```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}"; } ``` Η παγίδα: η Perl **δεν έχει τύπο συμβόλου**. Εκεί που η Ruby φτάνει στο `:apple` ως ένα ελαφρύ, εσωτερικευμένο κλειδί, η Perl απλώς χρησιμοποιεί τη συμβολοσειρά `"apple"` - και το παχύ κόμμα `=>` της βάζει αυτόματα εισαγωγικά, οπότε τα `apple => 3` και `$h{apple}` δεν χρειάζονται εισαγωγικά. Ένα σύμβολο της Ruby και μια συμβολοσειρά της Ruby είναι διαφορετικά αντικείμενα· στην Perl υπάρχει μόνο η συμβολοσειρά, οπότε η διάκριση `:sym` έναντι `"sym"` απλώς εξαφανίζεται. Ο έλεγχος μέλους είναι `exists $h{key}`, όχι το `key?` της Ruby, και ένα hash δεν έχει σειρά - επαναλάβετε με `sort keys %h` όταν θέλετε μια σταθερή. ## Περιβάλλον Αυτό είναι το κεφάλαιο που πρέπει να διαβάσετε δύο φορές. Η Ruby δεν έχει αντίστοιχο, και σχεδόν κάθε ιστορία «η Perl είναι παράξενη» ανάγεται σε αυτό. Κάθε έκφραση στην Perl τρέχει είτε σε **περιβάλλον λίστας** είτε σε **βαθμωτό περιβάλλον**, που καθορίζεται από το πού εμφανίζεται, όχι από το τι είναι. Ο ίδιος πίνακας συμπεριφέρεται διαφορετικά σε καθένα. Η ανάθεση ενός πίνακα σε ένα *βαθμωτό* δίνει το μήκος του· η ανάθεσή του σε μια *λίστα* (ένα σύνολο μεταβλητών σε παρενθέσεις) αντιγράφει στοιχεία. Η Ruby ποτέ δεν υπερφορτώνει την ανάθεση έτσι - το `a = arr` πάντα απλώς δίνει ψευδώνυμο στον πίνακα - οπότε αυτό είναι πραγματικά καινούργιο. ```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`. Το αντανακλαστικό της Ruby διαβάζει και τα δύο ως «ανάθεσε τον πίνακα», που είναι ακριβώς η παγίδα. Μια υπορουτίνα μπορεί να ρωτήσει σε ποιο περιβάλλον κλήθηκε, με `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` για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων. ## Αλήθεια τιμών Αυτή είναι η πιο απότομη ρήξη από τη Ruby σε όλον τον οδηγό. Στη Ruby μόνο τα `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 ``` Η παγίδα: τα `0` και `""` είναι **ψευδή** στην Perl αλλά **αληθή** στη Ruby - η πιο ανατρεπτική συνήθεια που πρέπει να ξεμάθετε. Ένας φύλακας της Ruby όπως το `if count` πυροδοτείται για `count == 0`· το `if ($count)` της Perl όχι. Από την άλλη πλευρά, τα `"00"` και `"0.0"` είναι **αληθή** στην Perl παρόλο που μοιάζουν αριθμητικά μηδέν, επειδή μόνο η ακριβής συμβολοσειρά `"0"` είναι ψευδής. Και σε αντίθεση με τη Ruby, όπου ίσως στηρίζεστε σε ελέγχους `nil`, η Perl ξεχωρίζει το «ψευδές» από το «απροσδιόριστο» με το defined-or `//`, που πέφτει πίσω μόνο στο `undef` (όχι στο `0` ή στο `""`): ```perl use v5.42; my %cfg = (host => "localhost"); my $port = $cfg{port} // 8080; # key absent -> undef -> 8080 say $port; # 8080 ``` ## Ροή ελέγχου Οι δεσμευμένες λέξεις είναι οικείες· η γραφή διαφέρει. Το `elsif` είναι το `elsif` της Perl (και το `elsif` της Ruby - μία από τις λίγες ακριβείς αντιστοιχίες), και τα μπλοκ είναι σε άγκιστρα, χωρίς `then` και χωρίς τερματικό `end`. ```ruby if n < 0 then sign = "neg" elsif n == 0 then sign = "zero" else sign = "pos" end nums.each { |x| puts x } while running 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 είναι το άμεσο αντίστοιχο των τελικών `puts x if cond` και `do_it while cond` της Ruby - μια απλή εντολή που φέρει τη δική της συνθήκη ή βρόχο: ```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`, ακριβώς όπως τα `unless`/`until` της Ruby: `say "ok" unless $error`. Η παγίδα: ο τριαδικός τελεστής χρησιμοποιεί `?`/`:`, όχι το `cond ? a : b` της Ruby - που είναι στην πραγματικότητα πανομοιότυπο, οπότε αυτό είναι δωρεάν. Δεν υπάρχει μορφή έκφρασης `x if c else y`· αλυσιδώστε τριαδικούς τελεστές αντ” αυτού: `my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos"`. ## Υπορουτίνες Το `def` γίνεται `sub`. Η σύγχρονη Perl έχει υπογραφές που μοιάζουν πολύ με τις λίστες παραμέτρων της Ruby, συμπεριλαμβανομένων των προεπιλογών. Και τα καλά νέα που θέλει να ακούσει ένας προγραμματιστής της Ruby: **η τελευταία αποτιμημένη έκφραση είναι η τιμή επιστροφής**, ακριβώς όπως στη Ruby - το `return` είναι προαιρετικό. ```ruby def greet(name, greeting = "Hello") "#{greeting}, #{name}!" end def total(*nums) nums.sum end ``` ```perl use v5.42; sub greet ($name, $greeting = "Hello") { return "$greeting, $name!"; } say greet("Ada"); # Hello, Ada! say greet("Yukihiro", "Hi"); # Hi, Yukihiro! sub area ($w, $h) { $w * $h } # no return needed; last expr wins say area(3, 4); # 12 sub total (@nums) { my $sum = 0; $sum += $_ for @nums; return $sum; } say total(1, 2, 3, 4); # 10 ``` Τα ονοματισμένα ορίσματα της Ruby αντιστοιχίζονται σε μια παράμετρο 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` (το αντανακλαστικό της Ruby μεταφέρεται), αλλά στην Perl αυτή η επιστροφή είναι επίσης **ευαίσθητη στο περιβάλλον** (δείτε [Περιβάλλον]()). Αν κάνετε `return @list`, ο καλών παίρνει τη λίστα σε περιβάλλον λίστας αλλά το *πλήθος* σε βαθμωτό περιβάλλον. Να είστε εσκεμμένοι σχετικά με το τι επιστρέφετε όταν μπορεί να χρησιμοποιηθεί με οποιονδήποτε τρόπο. ## Αναφορές Οι πίνακες και τα hashes της Perl δεν εμφωλεύονται απευθείας· τα εμφωλεύετε μέσω **αναφορών**, ενός βαθμωτού που δείχνει σε ένα σύνολο. Στη Ruby κάθε περιέκτης είναι ήδη αναφορά, οπότε η εμφώλευση είναι εκεί αόρατη σε εσάς· στην Perl παίρνετε μια αναφορά με `\` (ή φτιάχνετε μία με `[ ]`/`{ }`) και την ακολουθείτε με `->`. ```ruby data = {name: "Ada", langs: ["Perl", "Ruby"]} puts data[:langs][1] # Ruby data[:langs].push("Python") ``` ```perl use v5.42; my $data = { name => "Ada", langs => ["Perl", "Ruby"] }; say $data->{name}; # Ada say $data->{langs}[1]; # Ruby push $data->{langs}->@*, "Python"; # postfix deref to push say "@{$data->{langs}}"; # Perl Ruby Python ``` Το `{ ... }` φτιάχνει μια ανώνυμη αναφορά hash και το `[ ... ]` μια ανώνυμη αναφορά πίνακα - τα κυριολεκτικά δομικά στοιχεία των εμφωλευμένων δεδομένων, που αντιστοιχούν στις κυριολεξίες `{}` και `[]` της Ruby. Το βέλος `->` ακολουθεί μια αναφορά κατά ένα βήμα· ανάμεσα σε δύο δείκτες είναι προαιρετικό, οπότε τα `$data->{langs}[1]` και `$data->{langs}->[1]` είναι ίδια. Το όφελος για έναν προγραμματιστή της Ruby είναι τα blocks-ως-τιμές. Ένα block, `proc`, ή `lambda` της Ruby γίνεται μια ανώνυμη sub της Perl: ένα closure αποθηκευμένο σε ένα βαθμωτό, που καλείται με `->`. Κλείνει πάνω από τις λεξιλογικές του μεταβλητές ακριβώς όπως ένα block της Ruby: ```perl use v5.42; my $doubler = sub { map { $_ * 2 } @_ }; say join ",", $doubler->(1, 2, 3); # 2,4,6 my $factor = 10; my $scaler = sub ($x) { $x * $factor }; # closes over $factor say $scaler->(5); # 50 ``` Η παγίδα: μια ανώνυμη sub της Perl είναι μια τιμή που πρέπει να *αποθηκεύσετε και να καλέσετε ρητά* με `->( )` - δεν υπάρχει σιωπηρό `yield`, κανένα block που να περνιέται αόρατα δίπλα στα ορίσματα. Όταν μια ενσωματωμένη θέλει ένα block (`map`/`grep`/`sort`, ή `first`/`reduce` από το `List::Util`), γράφετε το `{ ... }` ενσωματωμένα· όταν θέλετε να περάσετε ένα καλέσιμο τριγύρω ως δεδομένα, φτιάχνετε ένα `sub { ... }` και το καλείτε μέσω του βαθμωτού του. Οι μεταθεματικές μορφές αποαναφοράς (`->@*`, `->%*`, `->$*`) είναι ο σύγχρονος, ευανάγνωστος τρόπος να αποαναφέρετε ένα ολόκληρο σύνολο. ## Κανονικές εκφράσεις Καλά νέα: το regex της Perl θα σας φανεί σαν στο σπίτι σας. Η Ruby δανείστηκε την κυριολεξία regex `/.../`, τον τελεστή `=~`, και τις μεταβλητές σύλληψης `$1`/`$2` κατευθείαν από την Perl. Ο τελεστής αντιστοίχισης `m//`, η αντικατάσταση `s///`, και οι ονομαστικές συλλήψεις στο `%+` το συμπληρώνουν. ```ruby s = "2026-06-20" if s =~ /(\d{4})-(\d{2})-(\d{2})/ puts "#{$1} #{$2} #{$3}" end t = "a,b,c".gsub(",", ";") 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` σε περιβάλλον λίστας επιστρέφει *όλες* τις συλλήψεις, το άμεσο αντίστοιχο του `String#scan` της Ruby: ```perl use v5.42; my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3 say "@nums"; ``` Η παγίδα: εκεί που το `gsub` της Ruby επιστρέφει μια νέα συμβολοσειρά και αφήνει την αρχική ήσυχη, το `s///` της Perl τροποποιεί τον στόχο του *επιτόπια* και επιστρέφει τον αριθμό των αντικαταστάσεων. Γι” αυτό το παράδειγμα αντιγράφει πρώτα με `(my $t = "a,b,c")` και μετά αντικαθιστά στο αντίγραφο. Για να πάρετε μια νέα συμβολοσειρά χωρίς να αγγίξετε την αρχική - τη συμπεριφορά `gsub` που περιμένετε - προσθέστε τη σημαία `/r`: `my $t = $s =~ s/,/;/gr`. ## I/O αρχείων και ροών Το ιδίωμα είναι το `open` με τρία ορίσματα και ένα λεξιλογικό filehandle, το αντίστοιχο του `File.open(path, mode) do |f| ... end` της Ruby. Ο τελεστής με γωνιώδεις αγκύλες `<$fh>` διαβάζει μια γραμμή, με τον ίδιο ρόλο όπως τα `f.each_line` ή `f.gets` της Ruby. ```ruby File.open("/tmp/sample.txt", "w") do |f| (1..3).each { |i| f.write("line #{i}\n") } end File.open("/tmp/sample.txt") do |f| f.each_line { |line| puts "got: #{line.chomp}" } end ``` ```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.chomp` της Ruby είναι το ίδιο ρήμα, και η Perl δάνεισε το όνομα. Και το `open` επιστρέφει ψευδές σε αποτυχία αντί να εγείρει εξαίρεση όπως κάνει το `File.open` της Ruby, οπότε το ιδίωμα `or die "...: $!"` είναι υποχρεωτικό· το `$!` είναι το μήνυμα σφάλματος συστήματος, το αντίστοιχο της Perl για το μήνυμα στην εξαίρεση `Errno` της Ruby. ## Αρθρώματα και πακέτα Τα `require`/`include` γίνονται `use`. Ο διαχωριστής χώρου ονομάτων είναι το `::`, που ήδη ξέρετε από σταθερές της Ruby όπως το `Set::Foo`· η Perl το χρησιμοποιεί παντού, και για πακέτα και για τα ονόματα μέσα τους. Ένα άρθρωμα `Foo::Bar` βρίσκεται στο `Foo/Bar.pm` στη διαδρομή ένταξης `@INC` (το `$LOAD_PATH` της Perl). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων, όπως όταν τραβάτε συγκεκριμένες μεθόδους εντός εμβέλειας. ```ruby require "set" nums = [1, 2, 3, 4].sum biggest = [3, 7, 2].max ``` ```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 ``` Η τυπική βιβλιοθήκη είναι μεγάλη, και πολλά από αυτά για τα οποία θα κατέφευγες στα RubyGems έρχονται στον πυρήνα ή στο CPAN, το αρχείο πακέτων της Perl. Τα `List::Util`, `Scalar::Util`, `POSIX`, `File::Spec`, και `Data::Dumper` είναι τα καθημερινά. Η παγίδα: το `use` τρέχει κατά τη *μεταγλώττιση*, όχι στο σημείο του αρχείου όπου εμφανίζεται, οπότε η σειρά των γραμμών `use` σε σχέση με τον κώδικά σας δεν έχει σημασία όπως μπορεί να έχει η τοποθέτηση του `require`. Η εισαγωγή είναι προαιρετική ανά όνομα: το `use List::Util qw(sum0 max)` φέρνει ακριβώς τα `sum0` και `max` στον χώρο ονομάτων σας, τίποτε άλλο - δεν υπάρχει monkey-patching ενσωματωμένων τύπων όπως η Ruby σας αφήνει να ξανανοίξετε το `Array`. ## Αντικείμενα Η Ruby είναι τα-πάντα-είναι-αντικείμενο· η Perl είναι πρακτική, και η διαφορά φάνηκε ήδη στους [Πίνακες]() (συναρτήσεις, όχι μέθοδοι). Αλλά όταν *όντως* θέλετε αντικείμενα, η Perl 5.42 έχει μια σύνταξη `class` πρώτης τάξης που θα φανεί οικεία. Τα πεδία δηλώνονται με `field`, οι μέθοδοι με `method`, και το `$self` είναι διαθέσιμο μέσα σε κάθε μέθοδο ακριβώς όπως το `self` στη Ruby (δεν το αναφέρετε ως παράμετρο). ```ruby class Point attr_accessor :x, :y def initialize(x: 0, y: 0) @x = x @y = y end def to_string "(#{@x}, #{@y})" end def move(dx, dy) @x += dx @y += dy self end end p = Point.new(x: 3, y: 4) puts p.to_string # (3, 4) p.move(1, 1) puts 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) ``` Ο κατασκευαστής δημιουργείται για εσάς, το αντίστοιχο του `attr_accessor` συν `initialize` της Ruby συμπτυγμένων μέσα στις δηλώσεις πεδίων: το `:param` σημειώνει ένα πεδίο που η αυτόματα κατασκευασμένη `new` δέχεται ως ονοματισμένο όρισμα, και το `= 0` είναι η προεπιλογή του. Η κληρονομικότητα είναι `:isa`, το αντίστοιχο του `class Point3D < Point` της Ruby: ```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 συμπεριφέρεται ακριβώς όπως φαίνεται παραπάνω· απλώς αναμένετε τις ειδοποιήσεις στη ροή σφαλμάτων. Σημειώστε επίσης ότι το `field` σας δίνει έναν `:reader` για πρόσβαση ανάγνωσης αλλά κανέναν setter τύπου `attr_accessor` της Ruby από προεπιλογή - γράφετε μια `method` για να μεταλλάξετε, όπως κάνει το `move` παραπάνω. Για το παλαιότερο μοντέλο αντικειμένων βασισμένο στο `bless` που θα συναντήσετε σε υπάρχοντα κώδικα, και για ρόλους, λεπτομέρειες κληρονομικότητας, και μετάβαση, δείτε τον [οδηγό Αντικειμενοστραφούς Προγραμματισμού](../oop/index.md). ## Χειρισμός σφαλμάτων Το `begin`/`rescue`/`ensure` της Ruby έχει δύο αντίστοιχα. Το κλασικό τυλίγει κώδικα σε `eval { }` και εξετάζει το `$@` (το σφάλμα που πιάστηκε) μετά - σημειώστε ότι το `eval` εδώ είναι *παγίδευση* εξαίρεσης, όχι το `eval` μιας συμβολοσειράς της Ruby: ```ruby begin risky(-1) rescue => e puts "caught: #{e.message}" end ``` ```perl use v5.42; my $result = eval { risky(-1) }; if ($@) { print "caught: $@"; # caught: negative! } ``` Η σύγχρονη μορφή είναι το `try`/`catch`/`finally`, που διαβάζεται σχεδόν ακριβώς όπως το `begin`/`rescue`/`ensure`: ```perl use v5.42; use feature 'try'; try { risky(-4); } catch ($e) { print "caught: $e"; # caught: negative! } finally { say "done"; # done (runs either way, like ensure) } ``` όπου η πλευρά που εγείρει είναι το `die` - το `raise` της 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"`. Τελειώστε τις συμβολοσειρές σφάλματός σας με `\n` όταν θέλετε ένα καθαρό μήνυμα και παραλείψτε το όταν θέλετε το ίχνος αρχείου-και-γραμμής. Το μπλοκ `finally`, όπως το `ensure` της Ruby, εκτελείται είτε ρίχτηκε εξαίρεση είτε όχι. Σημειώστε επίσης τις δύο μεταβλητές σφάλματος: το `$@` είναι η τελευταία εξαίρεση που πιάστηκε (το `rescue => e` της Ruby), ενώ το `$!` είναι το τελευταίο σφάλμα *συστήματος* (ένα αποτυχημένο `open`). Υπό το `pperl`, το χαρακτηριστικό `try`/`catch`/`finally` τυπώνει μια ειδοποίηση «experimental» στο stderr· το stdout δεν επηρεάζεται. ## Ιδιωματισμοί Μερικά μοτίβα που θα βλέπετε καθημερινά και θα θέλετε στα δάχτυλά σας. Ταξινομήστε μια λίστα με βάση ένα υπολογισμένο κλειδί. Η Ruby έχει το `sort_by` ενσωματωμένο· η φτιαγμένη-με-το-χέρι μορφή της Perl είναι ο Schwartzian transform - διακόσμησε, ταξινόμησε, αφαίρεσε τη διακόσμηση - και αξίζει να την αναγνωρίζετε με την πρώτη ματιά: ```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 από μια λίστα, το αντίστοιχο του `words.to_h { |w| [w, w.length] }` της Ruby: ```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` - τον ρόλο που παίζουν τα `ruby -e` και `ruby -ne` για κόλλημα κελύφους. Το μονόγραμμο με σιωπηρό βρόχο είναι το καθημερινό αντανακλαστικό του προγραμματιστή της Perl: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` Η παγίδα: διαβάστε τον [οδηγό Μονόγραμμων](../oneliner/index.md) για τους διακόπτες (`-n`, `-p`, `-a`, `-l`, `-e`) και τις δεκάδες συνταγές που είναι χτισμένες πάνω τους· είναι ένα από τα πράγματα με τη μεγαλύτερη μόχλευση που μπορείτε να μάθετε ερχόμενοι από υπόβαθρο κελύφους-και-Ruby. ## Παγίδες ερχόμενοι από τη Ruby Οι παγίδες, συγκεντρωμένες για μια τελική σάρωση πριν αρχίσετε να γράφετε: - **Τα sigils σημειώνουν σχήμα, όχι εμβέλεια.** Τα `$x`/`@x`/`%x` λένε μία/λίστα/αντιστοίχιση· δεν είναι οι δείκτες στιγμιότυπου/καθολικής/τοπικής της Ruby. Ένα στοιχείο του `@a` είναι `$a[0]`· μία τιμή του `%h` είναι `$h{k}`. - **Δεν είναι τα πάντα αντικείμενο.** Τα `uc "foo"` και `sum @a` είναι συναρτήσεις, όχι μέθοδοι σε μια συμβολοσειρά ή πίνακα. Καλείτε μεθόδους μόνο σε αναφορές που έχουν γίνει bless. - **Η αλήθεια είναι η μεγάλη υπόθεση.** Τα `0` και `""` είναι **ψευδή** στην Perl αλλά **αληθή** στη Ruby. Το `if ($count)` είναι ψευδές όταν το `$count` είναι `0`. Μόνο τα `0`, `""`, `"0"`, και `undef` είναι ψευδή· τα `"00"` και `"0.0"` είναι **αληθή**. - **Καθόλου σύμβολα.** Το `:name` δεν έχει αντίστοιχο στην Perl· τα κλειδιά hash είναι σκέτες συμβολοσειρές, και το `=>` βάζει αυτόματα εισαγωγικά στο bareword στα αριστερά του. - **Το `.` ενώνει συμβολοσειρές, το `+` προσθέτει αριθμούς.** Ποτέ μην κάνετε `+` δύο συμβολοσειρές για να τις συνενώσετε· αυτό τις εξαναγκάζει σε αριθμούς και προειδοποιεί. Η επανάληψη συμβολοσειράς είναι `x`, όχι `*`. - **Δύο οικογένειες σύγκρισης.** `==`/`<`/`<=>` για αριθμούς, `eq`/`lt`/ `cmp` για συμβολοσειρές. Επιλέξτε με βάση τον τύπο του τελεστέου, όχι με βάση τη μεταβλητή - σε αντίθεση με το `==` της Ruby που αποστέλλει με βάση την κλάση. - **Το `sort` ταξινομεί ως συμβολοσειρές από προεπιλογή.** Περάστε `{ $a <=> $b }` για αριθμητική σειρά. - **Περιβάλλον.** Το `my $x = @a` είναι το *μήκος*· το `my ($x) = @a` είναι το *πρώτο στοιχείο*. Οι παρενθέσεις είναι όλη η διαφορά. Ξαναδιαβάστε το [Περιβάλλον]() όταν μια τιμή επιστρέφει με λάθος σχήμα. - **Το `s///` επεξεργάζεται επιτόπια** και επιστρέφει ένα πλήθος, σε αντίθεση με το `gsub` της Ruby· χρησιμοποιήστε τη σημαία `/r` για να πάρετε μια νέα συμβολοσειρά αντ” αυτού. - **Το `open` δεν εγείρει εξαίρεση.** Χρησιμοποιήστε `open ... or die "...: $!"`. - **Τα blocks δεν είναι σιωπηρά.** Ένα αποθηκευμένο `sub { ... }` καλείται ρητά με `->( )`· δεν υπάρχει `yield` και κανένα αόρατο όρισμα block. - **Η μη-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.