מ־Ruby ל־Perl#
אתם מכירים את Ruby. אתם מעבירים בלוקים אל 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המקבלים בלוק.Hashים - ה־
Hashשלכם, ולאן הולכים הסמלים.הקשר - המושג שאין ל־Ruby מילה עבורו. קראו את זה.
אמיתות - מה נחשב לשקר, וזה נבדל בחדות.
בקרת זרימה -
if, לולאות, הצורות הסופיתיות.תת־שגרות -
defהופך ל־sub.הפניות - נתונים מקוננים, ובלוקים כערכים.
ביטויים רגולריים - regex כתחביר, כמו של Ruby.
קלט/פלט של קבצים וזרמים -
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, כמו חלוקת ה־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 הם החלק העשיר ביותר במיפוי הזה. map, select, ו־sort_by הופכים ל־map, grep, ו־sort של Perl, המקבלים בלוק { ... } בדיוק כפי שמתודות Ruby עושות. בתוך הבלוק, $_ הוא האיבר הנוכחי - משתנה הבלוק המובלע של 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 הוא אחיו של המחרוזות).
האשים#
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
המלכודת: תת־שגרה מחזירה את ערך הביטוי האחרון שלה גם בלי return (הרפלקס של Ruby ממשיך לכאן), אבל ב־Perl ההחזרה הזו גם רגישת־הקשר (ראו הקשר). אם תכתבו return @list, הקורא מקבל את הרשימה בהקשר רשימה אבל את המניין בהקשר סקלר. היו מכוונים לגבי מה שאתם מחזירים כשהוא עשוי לשמש בכל אחת מהדרכים.
הפניות#
המערכים וה־hashים של 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 היא בלוקים־כערכים. בלוק של Ruby, proc, או lambda הופך לתת־שגרה אנונימית של Perl: סגירה המאוחסנת בסקלר, נקראת עם ->. היא נסגרת על הלקסיקליים שלה בדיוק כמו בלוק של 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
המלכודת: תת־שגרה אנונימית של Perl היא ערך שעליכם לאחסן ולקרוא במפורש עם ->( ) - אין yield מובלע, אין בלוק המועבר בלתי־נראה לצד הארגומנטים. כשפונקציה מובנית רוצה בלוק (map/grep/sort, או first/reduce מ־List::Util), אתם כותבים את ה־{ ... } בשורה; כשאתם רוצים להעביר ניתן־לקריאה כנתונים, אתם יוצרים sub { ... } וקוראים לו דרך הסקלר שלו. צורות ה־deref הסופיתיות (->@*, ->%*, ->$*) הן הדרך המודרנית והקריאה לבצע dereference לצבר שלם.
ביטויים רגולריים#
חדשות טובות: ה־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.
קלט/פלט של קבצים וזרמים#
הניב הוא ה־open בעל שלושת הארגומנטים עם ידית קובץ לקסיקלית, המקבילה של ה־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;
כדי לבלוע את כל השורות בבת אחת, קראו את הידית בהקשר רשימה:
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 שתפגשו בקוד קיים, ועבור roles, פרטי הורשה, והגירה, ראו את מדריך התכנות מונחה־העצמים.
טיפול בשגיאות#
ל־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 היא טרנספורם שוורץ - קישוט, מיון, הסרת קישוט - וכדאי לזהות אותה במבט:
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 ממלאים כדבק shell. שורת־היחיד עם הלולאה המובלעת היא הרפלקס היומיומי של מפתח Perl:
pperl -lane '$s += $F[-1]; END { print $s }' data.txt
המלכודת: קראו את מדריך שורות־היחיד עבור המתגים (-n, -p, -a, -l, -e) ועשרות המתכונים הבנויים עליהם; זהו אחד הדברים בעלי המנוף הגבוה ביותר ללמוד בהגעה מרקע של shell ו־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 "...: $!".בלוקים אינם מובלעים.
sub { ... }מאוחסן נקרא במפורש עם->( ); איןyieldואין ארגומנט בלוק בלתי־נראה.מקור שאינו ASCII זקוק ל־
use utf8;. תחתuse v5.42;בית שאינו ASCII כליטרל במקור שלכם הוא שגיאת הידור בלעדיו. שמרו על נתוני מחרוזות כ־ASCII אלא אם תוסיפו במפורשuse utf8;.The
classfeature prints experimental notices to stderr onpperl. Correct output, noisy error stream.