Από 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:
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 απλώς χρησιμοποιεί ένα σκέτο όνομα.
name = "Ada"
langs = ["Ruby", "Perl"]
ages = {"Ada" => 36, "Yukihiro" => 60}
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 έχει ένα άμεσο αντίστοιχο για σκέτες μεταβλητές και ένα ελαφρώς μακρύτερο για αυθαίρετες εκφράσεις. Η συνένωση είναι ., όχι +.
name = "Ada"
greeting = "Hello, #{name}"
total = "Total: #{6 * 7}"
shout = "AB" * 3
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.
puts 7.0 / 2 # 3.5
puts 7 / 2 # 3 (integer division in Ruby)
puts 2 ** 10 # 1024
puts 10 % 3 # 1
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 - ο πίνακας είναι όρισμα, όχι αποδέκτης.
nums = [1, 2, 3, 4, 5]
nums.push(6)
last = nums.pop # 6
puts nums.length # 5
puts nums[1, 2].join(" ") # 2 3
use v5.42;
my @nums = (1, 2, 3, 4, 5);
push @nums, 6;
my $last = pop @nums; # 6
say scalar @nums; # 5
say "@nums[1,2]"; # 2 3 (a slice)
Οι επαναλήπτες της Ruby που δέχονται block είναι το πλουσιότερο μέρος αυτής της αντιστοίχισης. Τα map, select, και sort_by γίνονται map, grep, και sort της Perl, που δέχονται ένα block { ... } ακριβώς όπως οι μέθοδοι της Ruby. Μέσα στο block, το $_ είναι το τρέχον στοιχείο - η σιωπηρή μεταβλητή block της Perl, ο ρόλος που παίζει η παράμετρος |x| της Ruby.
squares = nums.map { |x| x * x }
evens = nums.select { |x| x.even? }
desc = nums.sort { |a, b| b <=> a }
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 στα αριστερά του.
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}" }
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 πάντα απλώς δίνει ψευδώνυμο στον πίνακα - οπότε αυτό είναι πραγματικά καινούργιο.
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, και να επιστρέψει διαφορετικά πράγματα ανάλογα:
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:
use v5.42;
my $n = () = "a1b2c3" =~ /\d/g;
say $n; # 3
Η παγίδα: δεν υπάρχει μία μόνο απάντηση στο «τι κάνει αυτός ο πίνακας» - εξαρτάται από την περιβάλλουσα έκφραση. Όταν κάτι επιστρέφει έναν αριθμό που περιμένατε να είναι λίστα (ή το αντίστροφο), το περιβάλλον είναι ο πρώτος ύποπτος. Φτάστε στο scalar για να επιβάλετε ένα πλήθος και σε παρενθέσεις στα αριστερά για να επιβάλετε αντιγραφή στοιχείων.
Αλήθεια τιμών#
Αυτή είναι η πιο απότομη ρήξη από τη Ruby σε όλον τον οδηγό. Στη Ruby μόνο τα nil και false είναι ψευδή - τα 0 και "" είναι και τα δύο αληθή. Στην Perl οι ψευδείς τιμές είναι μια σύντομη, συγκεκριμένη λίστα: ο αριθμός 0, η κενή συμβολοσειρά "", η συμβολοσειρά "0", και το undef (το nil της Perl). Όλα τα άλλα είναι αληθή.
use v5.42;
for my $v (0, 1, "", "0", "00", "0.0", "false") {
say "'$v' is ", ($v ? "true" : "false");
}
Αυτό τυπώνει, ακριβώς:
'0' is false
'1' is true
'' is false
'0' is false
'00' is true
'0.0' is true
'false' is true
Η παγίδα: τα 0 και "" είναι ψευδή στην Perl αλλά αληθή στη Ruby - η πιο ανατρεπτική συνήθεια που πρέπει να ξεμάθετε. Ένας φύλακας της Ruby όπως το if count πυροδοτείται για count == 0· το if ($count) της Perl όχι. Από την άλλη πλευρά, τα "00" και "0.0" είναι αληθή στην Perl παρόλο που μοιάζουν αριθμητικά μηδέν, επειδή μόνο η ακριβής συμβολοσειρά "0" είναι ψευδής. Και σε αντίθεση με τη Ruby, όπου ίσως στηρίζεστε σε ελέγχους nil, η Perl ξεχωρίζει το «ψευδές» από το «απροσδιόριστο» με το defined-or //, που πέφτει πίσω μόνο στο undef (όχι στο 0 ή στο ""):
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.
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
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 - μια απλή εντολή που φέρει τη δική της συνθήκη ή βρόχο:
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 είναι προαιρετικό.
def greet(name, greeting = "Hello")
"#{greeting}, #{name}!"
end
def total(*nums)
nums.sum
end
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 συγκεντρώνει:
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 παίρνετε μια αναφορά με \ (ή φτιάχνετε μία με [ ]/{ }) και την ακολουθείτε με ->.
data = {name: "Ada", langs: ["Perl", "Ruby"]}
puts data[:langs][1] # Ruby
data[:langs].push("Python")
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:
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///, και οι ονομαστικές συλλήψεις στο %+ το συμπληρώνουν.
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(",")
use v5.42;
my $s = "2026-06-20";
if ($s =~ /(\d{4})-(\d{2})-(\d{2})/) {
say "$1 $2 $3"; # 2026 06 20
}
if ($s =~ /(?<year>\d{4})/) {
say $+{year}; # 2026
}
(my $t = "a,b,c") =~ s/,/;/g; # a;b;c
my @parts = split /,/, "one,two,three"; # one two three
Μια αντιστοίχιση /g σε περιβάλλον λίστας επιστρέφει όλες τις συλλήψεις, το άμεσο αντίστοιχο του String#scan της Ruby:
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.
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
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 σε περιβάλλον λίστας:
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). Η λίστα εισαγωγής ενός αρθρώματος είναι μια επιλογή ονομάτων, όπως όταν τραβάτε συγκεκριμένες μεθόδους εντός εμβέλειας.
require "set"
nums = [1, 2, 3, 4].sum
biggest = [3, 7, 2].max
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 (δεν το αναφέρετε ως παράμετρο).
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)
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:
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 που θα συναντήσετε σε υπάρχοντα κώδικα, και για ρόλους, λεπτομέρειες κληρονομικότητας, και μετάβαση, δείτε τον οδηγό Αντικειμενοστραφούς Προγραμματισμού.
Χειρισμός σφαλμάτων#
Το begin/rescue/ensure της Ruby έχει δύο αντίστοιχα. Το κλασικό τυλίγει κώδικα σε eval { } και εξετάζει το $@ (το σφάλμα που πιάστηκε) μετά - σημειώστε ότι το eval εδώ είναι παγίδευση εξαίρεσης, όχι το eval μιας συμβολοσειράς της Ruby:
begin
risky(-1)
rescue => e
puts "caught: #{e.message}"
end
use v5.42;
my $result = eval { risky(-1) };
if ($@) {
print "caught: $@"; # caught: negative!
}
Η σύγχρονη μορφή είναι το try/catch/finally, που διαβάζεται σχεδόν ακριβώς όπως το begin/rescue/ensure:
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:
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 - διακόσμησε, ταξινόμησε, αφαίρεσε τη διακόσμηση - και αξίζει να την αναγνωρίζετε με την πρώτη ματιά:
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:
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:
pperl -lane '$s += $F[-1]; END { print $s }' data.txt
Η παγίδα: διαβάστε τον οδηγό Μονόγραμμων για τους διακόπτες (-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
classfeature prints experimental notices to stderr onpperl. Correct output, noisy error stream.