# מ־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: ```perl 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 פשוט משתמשת בשם חשוף. ```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, כמו חלוקת ה־`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 הם החלק העשיר ביותר במיפוי הזה. `map`, `select`, ו־`sort_by` הופכים ל־`map`, `grep`, ו־`sort` של Perl, המקבלים בלוק `{ ... }` בדיוק כפי שמתודות Ruby עושות. בתוך הבלוק, `$_` הוא האיבר הנוכחי - משתנה הבלוק המובלע של 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` הוא אחיו של המחרוזות). ## האשים 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 ``` המלכודת: תת־שגרה מחזירה את ערך הביטוי האחרון שלה גם בלי `return` (הרפלקס של Ruby ממשיך לכאן), אבל ב־Perl ההחזרה הזו גם **רגישת־הקשר** (ראו [הקשר]()). אם תכתבו `return @list`, הקורא מקבל את הרשימה בהקשר רשימה אבל את *המניין* בהקשר סקלר. היו מכוונים לגבי מה שאתם מחזירים כשהוא עשוי לשמש בכל אחת מהדרכים. ## הפניות המערכים וה־hashים של 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 היא בלוקים־כערכים. בלוק של Ruby, `proc`, או `lambda` הופך לתת־שגרה אנונימית של Perl: סגירה המאוחסנת בסקלר, נקראת עם `->`. היא נסגרת על הלקסיקליים שלה בדיוק כמו בלוק של 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 ``` המלכודת: תת־שגרה אנונימית של Perl היא ערך שעליכם *לאחסן ולקרוא במפורש* עם `->( )` - אין `yield` מובלע, אין בלוק המועבר בלתי־נראה לצד הארגומנטים. כשפונקציה מובנית רוצה בלוק (`map`/`grep`/`sort`, או `first`/`reduce` מ־`List::Util`), אתם כותבים את ה־`{ ... }` בשורה; כשאתם רוצים להעביר ניתן־לקריאה כנתונים, אתם יוצרים `sub { ... }` וקוראים לו דרך הסקלר שלו. צורות ה־deref הסופיתיות (`->@*`, `->%*`, `->$*`) הן הדרך המודרנית והקריאה לבצע dereference לצבר שלם. ## ביטויים רגולריים חדשות טובות: ה־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`. ## קלט/פלט של קבצים וזרמים הניב הוא ה־`open` בעל שלושת הארגומנטים עם ידית קובץ לקסיקלית, המקבילה של ה־`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; ``` כדי לבלוע את כל השורות בבת אחת, קראו את הידית בהקשר רשימה: ```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` שתפגשו בקוד קיים, ועבור roles, פרטי הורשה, והגירה, ראו את [מדריך התכנות מונחה־העצמים](../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 היא טרנספורם שוורץ - קישוט, מיון, הסרת קישוט - וכדאי לזהות אותה במבט: ```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` ממלאים כדבק shell. שורת־היחיד עם הלולאה המובלעת היא הרפלקס היומיומי של מפתח Perl: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` המלכודת: קראו את [מדריך שורות־היחיד](../oneliner/index.md) עבור המתגים (`-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 `class` feature prints experimental notices to stderr** on `pperl`. Correct output, noisy error stream.