# מ־Lua ל־Perl אתם מכירים את Lua. אתם בונים table בלי לחשוב, אתם פונים אל `setmetatable` כדי לקבל אובייקטים ואופרטורים, ו־`for k, v in pairs(t)` הוא זיכרון שרירי. המדריך הזה ממפה כל אחד מההרגלים האלה אל Perl, כך שתוכלו להיות יצרניים ב־`pperl` תוך אחר צהריים אחד במקום ללמוד מחדש תכנות מאפס. שני זעזועים נוחתים בחמש הדקות הראשונות, אז הכירו אותם עכשיו: - **ה־table היחיד של Lua הופך לשני טיפוסים של Perl.** ל־Lua יש צבר יחיד, ה־table, שאתם משתמשים בו גם כמערך (`{1, 2, 3}`) וגם כמיפוי (`{k = "v"}`). Perl מפצלת זאת לשני דברים נבדלים: `@array` מסודר הממופתח לפי מספרים שלמים ו־`%hash` הממופתח לפי מחרוזות. ההחלטה ”האם זו רשימה או חיפוש?“ מראש היא ההרגל החדש המרכזי, וחלקי [המערכים]() ו[ה־Hashים]() אינם עוסקים בשום דבר אחר. - **משתנים עוטים sigils.** שמות ב־Lua חשופים: `x`, `t`, `f`. ב־Perl סקלר הוא `$x`, מערך הוא `@x`, hash הוא `%x`. ה־sigil הוא חלק מהאופן שבו אתם קוראים את המשתנה, לא רק קישוט. זהו ההבדל השטחי הגדול ביותר מ־Lua, וחלק ראשון אינו עוסק בשום דבר אחר. 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` (כל משתנה חייב להיות מוצהר עם `my`, התרופה למשתנים הגלובליים המובלעים מועדי־התאונות של Lua), את `warnings`, את הפונקציה המובנית `say` (זהו `print` עם תו שורה חדשה בסופו, כמו `print` של Lua), חתימות של תת־שגרות, ואת מערכת התכונות המודרנית המשמשת לאורך כל המדריך הזה. ## כיצד לקרוא את זה - [משתנים ו־sigils]() - המעבר בין `$`/`@`/`%`, ו־`local x` ההופך ל־`my $x`. - [מחרוזות]() - שִחלוף, ושרשור `..` ההופך ל־`.`. - [מספרים]() - טיפוס שטחי אחד, חלוקה צפה. - [מערכים ורשימות]() - חצי מה־table שלכם, בסיס־0 לא בסיס־1. - [Hashים]() - החצי השני של ה־table שלכם. - [הקשר]() - המושג שאין ל־Lua מילה עבורו. קראו את זה. - [אמיתות]() - מה נחשב לשקר, וזה נבדל בחדות מ־Lua. - [בקרת זרימה]() - `if`, לולאות, הצורות הסופיתיות. - [תת־שגרות]() - `function` הופך ל־`sub`; closures אמיתיים. - [הפניות]() - נתונים מקוננים ו־closures. - [ביטויים רגולריים]() - regex אמיתי, לא תבניות Lua. - [קלט/פלט של קבצים וזרמים]() - `open`, קריאות שורה, slurp. - [מודולים וחבילות]() - `require` הופך ל־`use`. - [אובייקטים]() - metatables הופכים לתחביר ה־`class` המודרני. - [טיפול בשגיאות]() - `pcall` הופך ל־`eval` או ל־`try`/`catch`. - [ניבים]() - שורות־היחיד היומיומיות. - [מלכודות בהגעה מ־Lua]() - סרקו לפני שאתם כותבים. ## משתנים ו־sigils ב־Lua שם פשוט נותן שם לערך, תהא צורתו אשר תהא. ב־Perl ה**sigil מספר לכם את הצורה**: `$` עבור ערך יחיד, `@` עבור רשימה מסודרת, `%` עבור מיפוי מפתח/ערך. `my` מצהיר על משתנה בעל היקף לקסיקלי, התפקיד המדויק של ה־`local` של Lua. במקום ש־Lua מאפשרת להשמה חשופה ליצור גלובלי בשקט, `use v5.42` הופך זאת לשגיאת הידור: כל משתנה הוא `my` אלא אם תאמרו אחרת. ```lua local name = "Ada" local langs = {"Lua", "Perl"} local ages = {Ada = 36, Linus = 56} ``` ```perl use v5.42; my $name = "Ada"; my @langs = ("Lua", "Perl"); my %ages = (Ada => 36, Linus => 56); say $name; # Ada say $langs[0]; # Lua say $ages{Ada}; # 36 ``` המלכודת: כשאתם נשלחים *לתוך* צבר כדי לקבל איבר אחד, ה־sigil עוקב אחר הערך שאתם מקבלים בחזרה, לא אחר המכל. איבר יחיד של `@langs` הוא סקלר, ולכן הוא `$langs[0]`, לא `@langs[0]`; ערך אחד מתוך `%ages` הוא `$ages{Ada}`. צורת ה־`@`/`%` היא האוסף כולו; צורת ה־`$` היא חלק אחד ממנו. ב־Lua שם ה־table ‏`t` לעולם אינו משתנה בין שאתם כותבים `t`, `t[1]`, או `t.key`; ב־Perl הסימן המוביל משתנה לפי מה שאתם ניגשים אליו. ## מחרוזות מירכאות כפולות משחלפות משתנים, מירכאות בודדות אינן עושות זאת. אין צורך ברפלקס ה־`string.format` למקרה הנפוץ, מפני שמחרוזת ברירת המחדל כבר משחלפת. ההחלפה הגדולה: שרשור הוא `.`, במקום שבו Lua משתמשת ב־`..`. ```lua local name = "Ada" local greeting = "Hello, " .. name local shout = string.rep("AB", 3) ``` ```perl use v5.42; my $name = "Ada"; my $greeting = "Hello, $name"; # Hello, Ada my $literal = 'Hello, $name'; # Hello, $name (no interpolation) my $joined = "AB" . "CD"; # ABCD my $shout = "AB" x 3; # ABABAB ``` המלכודת: אופרטור השרשור `..` של Lua אינו קיים ב־Perl; הנקודה הבודדת `.` מצרפת מחרוזות והאופרטור `x` משכפל אותן (ה־`string.rep` של Lua). גם איברי מערך וערכי hash משוחלפים בתוך מירכאות כפולות, כך ש־`"First: $langs[0]"` עובד כפי שנכתב, מה שמסיר את רוב שרשור ה־`..` שמפתח Lua פונה אליו. ## מספרים ל־Perl יש טיפוס מספרי אחד ברמת השטח; אינכם בוחרים בין שלם ל־float, בדומה לכך ש־Lua 5.3+ מציגה `number` יחיד לרוב הקוד. חלוקה תמיד מייצרת float. ```lua print(7 / 2) -- 3.5 print(math.floor(7 / 2)) -- 3 print(2 ^ 10) -- 1024.0 print(10 % 3) -- 1 ``` ```perl use v5.42; say 7 / 2; # 3.5 say int(7 / 2); # 3 say 2 ** 10; # 1024 say 10 % 3; # 1 ``` המלכודת: אופרטורי ההשוואה והשוויון מגיעים בשתי משפחות, ואתם בוחרים לפי *טיפוס האופרנדים*, לא לפי זה של המשתנים. מספרים משתמשים ב־`==`, `!=`, `<`, `>`, `<=>`; מחרוזות משתמשות ב־`eq`, `ne`, `lt`, `gt`, `cmp`. `"10" == "10.0"` הוא אמת (מספרי), אבל `"10" eq "10.0"` הוא שקר (מחרוזת). ל־Lua יש רק `==` ואינה כופה המרה כלשהי על פני קו המחרוזת/מספר; Perl במקום זאת מבקשת *מכם* לבחור את ההשוואה, ושימוש ב־`==` על מחרוזות (או ב־`eq` על מספרים) הוא באג שקט תכוף. ## מערכים ורשימות הנה חצי מהפיצול הגדול של ה־table. רצף של Lua (`{10, 20, 30}`, ה־table המשמש כמערך) הופך ל־`@array` של Perl. ההבדל הראשון והרועש ביותר: **מערכי Perl הם בסיס־0**, במקום שמערכי Lua מתחילים על פי המוסכמה באינדקס 1. ```lua local t = {"x", "y", "z"} print(t[1]) -- x (Lua: first element is index 1) print(t[3]) -- z print(#t) -- 3 (length) ``` ```perl use v5.42; my @t = ("x", "y", "z"); say $t[0]; # x (Perl: first element is index 0) say $t[2]; # z say $t[-1]; # z (negative indices count from the end) say scalar @t; # 3 (length; @t in scalar context is its size) ``` המשנים משונים בשמם אבל מוכרים. `table.insert`/`table.remove` הופכים ל־`push`/`pop`/`shift`/`unshift`: ```perl use v5.42; my @nums = (10, 20, 30); push @nums, 40; # add to the end my $last = pop @nums; # 40, removed from the end say scalar @nums; # 3 say "@nums[0,1]"; # 10 20 (a slice) ``` Lua פונה ללולאות `for i = 1, #t do ... end` מפורשות כדי להמיר או לסנן; ל־Perl יש `map` (המרה) ו־`grep` (סינון), עם `$_` כאיבר הנוכחי: ```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 ``` המלכודת: מעבר לאינדוקס בסיס־0, ל־`#t` אין איות יחיד ב־Perl - אתם משתמשים ב־`scalar @a` עבור אורכו של מערך וב־`keys %h` עבור זה של hash. ו־`sort` משווה כמחרוזות כברירת מחדל, כך ש־`sort (10, 9, 100)` נותן `10 100 9`. עבור מספרים עליכם להעביר משווה: `sort { $a <=> $b } @nums`. `$a` ו־`$b` הם שני האיברים המושווים, ו־`<=>` הוא אופרטור התלת־כיווני המספרי (`cmp` הוא אחיו של המחרוזות). ## האשים הנה החצי השני של פיצול ה־table. table של Lua המשמש כמיפוי (`{name = "Ada", age = 36}`, או `t["key"]`) הופך ל־`%hash` של Perl. מוצהר עם `%`, ניגשים אליו ערך אחד בכל פעם עם `$h{key}`. ```lua local h = {apple = 3, pear = 5, plum = 2} print(h.apple) -- 3 print(h["pear"]) -- 5 h.plum = nil -- delete a key for k, v in pairs(h) do print(k, v) end ``` ```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}"; # apple => 3 ... pear => 5 } ``` המלכודת: Lua ממזגת את המערך ואת המיפוי ל־table אחד ושומרת בתוכו גם חלק מערך וגם חלק hash; Perl שומרת אותם בנפרד גמור, כך שאתם מחליטים מראש אם דבר הוא `@array` (מיקומים מספריים, מסודר) או `%hash` (מפתחות מחרוזת, לא מסודר). בדיקת חברוּת היא `exists $h{key}`, המקבילה של בדיקת ה־`h[k] ~= nil` של Lua, והפסיק השמן `=>` הוא מפריד המפתח/ערך הניבי המצטט אוטומטית את ה־bareword שמשמאלו (כך ש־`apple => 3` אינו זקוק למירכאות). ל־hash אין סדר, אז עברו `sort keys %h` כשאתם רוצים סדר יציב; ה־`pairs` של Lua אינו מסודר מאותה סיבה. ## הקשר זהו הפרק שכדאי לקרוא פעמיים. ל־Lua אין מקבילה כללית, וכמעט כל סיפור ”Perl מוזרה“ מתחקה בחזרה אליו. אבל Lua *כן* נותנת לכם את הגשר האחד שמבהיר זאת: **ערכי החזרה מרובים**. ב־Lua, ‏`local a, b = f()` מפזר את כמה ערכי ההחזרה של פונקציה על פני כמה משתנים, ו־`#t` או `select("#", ...)` מצמצמים רשימה למניין. Perl מכלילה את הרעיון הזה לתכונה של *כל* ביטוי. כל ביטוי רץ או ב**הקשר רשימה** או ב**הקשר סקלר**, הנקבע לפי המקום שבו הוא מופיע. אותו מערך עצמו מתנהג אחרת בכל אחד מהם: בהשמה אל *סקלר* הוא נותן את אורכו, בהשמה אל *רשימה* (משתנים בסוגריים) הוא מעתיק איברים. ```perl use v5.42; my @list = (10, 20, 30); my $count = @list; # scalar context -> 3 (the length) say $count; # 3 my ($first) = @list; # list context -> first element copied say $first; # 10 my ($a, $b) = @list; # 10 and 20 say "$a $b"; # 10 20 ``` הסוגריים שמשמאל הם כל ההבדל. `my $x = @list` שואל ”כמה?“ ומקבל `3`. `my ($x) = @list` שואל ”תן לי את האיברים“ ומקבל `10`. צורת הרשימה `my ($a, $b) = @list` היא בדיוק ה־`local a, b = ...` של Lua - הגשר שאתם כבר מכירים. תת־שגרה המחזירה כמה ערכים זהה לפונקציית Lua עם כמה ערכי `return`; הקשר הקורא מחליט מה חוזר: ```perl use v5.42; sub minmax (@nums) { my @s = sort { $a <=> $b } @nums; return ($s[0], $s[-1]); } my ($lo, $hi) = minmax(4, 1, 9, 2); say "$lo $hi"; # 1 9 ``` תת־שגרה יכולה אפילו לשאול באיזה הקשר היא נקראה, באמצעות `wantarray`, ולענות אחרת - משהו שפונקציות Lua אינן יכולות לעשות: ```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 ``` המלכודת: אין תשובה יחידה ל“מה המערך הזה עושה“ - זה תלוי בביטוי הסובב, במקום ש־Lua פותרת פיזור רב־ערכי באתר הקריאה הספציפי בלבד. כשמשהו מחזיר מספר שציפיתם שיהיה רשימה (או להפך), ההקשר הוא החשוד הראשון. פנו אל `scalar` כדי לאלץ מניין, ואל סוגריים משמאל כדי לאלץ העתקת איברים. ## אמיתיוּת זהו שבר חד מ־Lua, אז האטו. ב־Lua **רק `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 ``` המלכודת: האינסטינקט של מפתח Lua ש־`0` הוא אמת יהפוך בשקט את משמעותו של כל `if ($count)`. ב־Perl ‏`0` ו־`""` *הם* שקר, כך ש־`if ($n)` הוא שקר כש־`$n` הוא אפס - בדרך כלל מה שאתם רוצים, אבל ההפך מ־Lua. בכיוון השני, `"00"` ו־`"0.0"` הם **אמת** אף שהם נראים אפס מספרית, מפני שרק המחרוזת המדויקת `"0"` היא שקר. עבור בחירת ”השתמש בזה אם מוגדר, אחרת ברירת מחדל“ - ניב ה־`x = y or default` של Lua, הנכשל על `0` ו־`false` - השתמשו ב־defined-or `//` של Perl, שנופל לערך החלופי רק על `undef` (ה־`nil` של Perl): ```perl use v5.42; my %cfg = (host => "localhost"); my $port = $cfg{port} // 8080; # key absent -> undef -> 8080 say $port; # 8080 ``` ## זרימת בקרה מילות המפתח מוכרות; האיות נבדל. ה־`elseif` של Lua הוא ה־`elsif` של Perl, אין `then`/`do`/`end`, ובלוקים מוקפים בסוגריים מסולסלים במקום מתוחמים במילות מפתח. ```lua if n < 0 then sign = "neg" elseif n == 0 then sign = "zero" else sign = "pos" end for _, x in ipairs(nums) do print(x) end while running do 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 מוסיפה שני דברים שאין ל־Lua. **משני סופית** מאפשרים למשפט פשוט לשאת עליו את התנאי או הלולאה שלו עצמו, מה שנקרא בטבעיות עבור שורות־יחיד: ```perl use v5.42; say $_ for @nums; # loop, one statement say "negative" if $n < 0; # guarded statement $sum += $_ for @nums; # accumulate ``` ו־`unless`/`until` הם הצורות השליליות של `if`/`while`, למקרים שבהם הניסוח החיובי נקרא במגושמות: `say "ok" unless $error`. המלכודת: ל־Perl אין משפט `x if c else y`; ביטוי התנאי הוא האופרטור התלת־איברי בסגנון C עם `?`/`:`, ש־Lua בדרך כלל מזייפת עם `c and x or y`. צורת ה־Perl ישירה ומשתשרת: `my $sign = $n < 0 ? "neg" : $n == 0 ? "zero" : "pos"`. בקרת לולאה היא `last`/`next` במקום שבו ל־Lua יש `break` ו(ב־5.4) `goto continue`. ## תת־שגרות `function` הופך ל־`sub`. ל־Perl המודרנית יש חתימות הנותנות שם לפרמטרים שלהן, כולל ערכי ברירת מחדל, במקום ה־`...` המיקומי וטיפול ה־`arg` הידני של Lua. ```lua local function greet(name, greeting) greeting = greeting or "Hello" return greeting .. ", " .. name .. "!" end local function total(...) local sum = 0 for _, n in ipairs({...}) do sum = sum + n end return sum end ``` ```perl use v5.42; sub greet ($name, $greeting = "Hello") { return "$greeting, $name!"; } say greet("Ada"); # Hello, Ada! say greet("Linus", "Hi"); # Hi, Linus! sub total (@nums) { my $sum = 0; $sum += $_ for @nums; return $sum; } say total(1, 2, 3, 4); # 10 ``` ניב הארגומנט על שם של Lua (העברת table יחיד, `f{host=..., port=...}`) ממופה לפרמטר 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` (כמו ההחזרה המפורשת־בלבד של Lua, אלא של־Perl יש גם את צורת הערך־האחרון המובלעת), וההחזרה היא **רגישת־הקשר** (ראו [הקשר]()). אם תכתבו `return @list`, הקורא מקבל את הרשימה בהקשר רשימה אבל את *המניין* בהקשר סקלר - דרגת חופש שאין להחזרה־המרובה של Lua. ## הפניות המערכים וה־hashים של Perl אינם מקננים ישירות; אתם מקננים אותם דרך **הפניות**, סקלר המצביע אל צבר. ב־Lua כל table הוא כבר הפניה (השמת table משתפת אותו, לעולם אינה מעתיקה), ולכן המושג הזה בלתי נראה לכם שם; ב־Perl אתם לוקחים הפניה במפורש עם `\`, ועוקבים אחריה עם `->`. ```lua local data = {name = "Ada", langs = {"Perl", "Lua"}} print(data.langs[2]) -- Lua table.insert(data.langs, "Ruby") ``` ```perl use v5.42; my $data = { name => "Ada", langs => ["Perl", "Lua"] }; say $data->{name}; # Ada say $data->{langs}[1]; # Lua push $data->{langs}->@*, "Ruby"; # postfix deref to push say "@{$data->{langs}}"; # Perl Lua Ruby ``` `{ ... }` בונה הפניית hash אנונימית ו־`[ ... ]` הפניית מערך אנונימית - אבני הבניין הליטרליות של נתונים מקוננים. החץ `->` עוקב אחר הפניה צעד אחד; בין שני מנויים הוא רשות, כך ש־`$data->{langs}[1]` ו־`$data->{langs}->[1]` זהים. תת־שגרות אנונימיות הן closures אמיתיים, בדיוק כמו ה־closures של Lua על upvalues - אחד המעברים הנקיים ביותר בכל המדריך: ```perl use v5.42; sub counter ($start) { my $n = $start; return sub { return $n++ }; } my $c = counter(10); say $c->(); # 10 say $c->(); # 11 say $c->(); # 12 ``` המלכודת: צורות ה־deref הסופיתיות (`->@*`, `->%*`, `->$*`) הן הדרך המודרנית והקריאה לבצע dereference לצבר שלם, ו־`"@{ ... }"` הוא האופן שבו אתם משחלפים מערך מ־dereferenced אל מחרוזת. הצורות הישנות יותר `@$ref` ו־`${$ref}{key}` עדיין עובדות ומופיעות בקוד קיים, אבל פנו אל צורות החץ והסופית בקוד חדש. ## ביטויים רגולריים זהו שדרוג, לא תרגום. ל־Lua יש **תבניות** - `string.match`, `string.gsub`, מחלקות תווים כמו `%d` - שאינן במכוון ביטויים רגולריים וחסרות חלופיות, כמתים, והפניות לאחור. ל־Perl יש regex מלא כ**תחביר**: `=~` קושר תבנית למחרוזת, `m//` מתאים, `s///` מציב. קבוצות לכידה נוחתות ב־`$1`, `$2`, ולכידות על שם ב־`%+`. ```lua local s = "2026-06-20" local y, m, d = string.match(s, "(%d+)-(%d+)-(%d+)") local t = string.gsub("a,b,c", ",", ";") -- a;b;c ``` ```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.gmatch`: ```perl use v5.42; my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3 say "@nums"; ``` המלכודת: התווים המטא נבדלים מתבניות Lua - Perl משתמשת ב־`\d` במקום ש־Lua משתמשת ב־`%d`, בכמתים `*`/`+`/`?`, בחלופיות `|`, ובקבוצות על שם `(?...)`, שאף אחת מהן אינה נתמכת בתבניות Lua. ו־`s///` משנה את היעד שלו *במקום* ומחזיר את מספר ההצבות, לא מחרוזת חדשה, במקום שבו ה־`gsub` של Lua מחזיר מחרוזת חדשה. בגלל זה הדוגמה מעתיקה תחילה עם `(my $t = "a,b,c")` ואז מציבה על העותק. כדי לקבל מחרוזת חדשה בלי לגעת במקור, השתמשו בדגל `/r`: `my $t = $s =~ s/,/;/gr`. ## קלט/פלט של קבצים וזרמים הניב הוא ה־`open` בעל שלושת הארגומנטים עם ידית קובץ לקסיקלית, המקבילה של ה־`io.open` של Lua. אופרטור הסוגריים המשולשים `<$fh>` קורא שורה, תפקידם של ה־`fh:read("l")` או איטרטור ה־`fh:lines()` של Lua. ```lua local f = io.open("/tmp/sample.txt", "w") for i = 1, 3 do f:write("line " .. i .. "\n") end f:close() f = io.open("/tmp/sample.txt", "r") for line in f:lines() do print("got:", line) end f:close() ``` ```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>` שומרת על תו השורה החדשה שבסופה (ה־`fh:lines()` של Lua מסיר אותו, `fh:read("L")` שומר אותו); קראו ל־`chomp` כדי להסירו. ו־`open` מחזיר שקר בכישלון במקום לזרוק, בדומה מאוד ל־`io.open` של Lua המחזיר `nil, err`, ולכן הניב `or die "...: $!"` הוא חובה - `$!` הוא הודעת שגיאת המערכת, ה־`errno` של Perl. ## מודולים וחבילות ה־`require` של Lua הופך ל־`use` של Perl. מפריד מרחב השמות הוא `::`, לא `.`, ומודול `Foo::Bar` שוכן ב־`Foo/Bar.pm` על נתיב ההכללה `@INC` (ה־`package.path` של Lua). בניגוד ל־`require` של Lua, המחזיר table שאתם משימים בעצמכם, `use` מייבא שמות נבחרים ישירות אל מרחב השמות שלכם. ```lua local sqrt = math.sqrt local sum = 0 for i = 1, 10 do sum = sum + i end ``` ```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 ``` הנה הפתעה נעימה בהגעה מהספרייה הסטנדרטית הזעירה במכוון של Lua: Perl מגיעה עם מערכת מובנית גדולה, והרבה ממה שהייתם עושים ביד או מושכים מ־LuaRocks נמצא בליבה או במרחק `use` אחד ב־CPAN, ארכיון החבילות של Perl. `List::Util`, `Scalar::Util`, `POSIX`, `File::Spec`, ו־`Data::Dumper` הם היומיומיים. המלכודת: `use` רץ ב *זמן ההידור*, לא בשורה שבה הוא מופיע, ולכן מיקומו ביחס לקוד שלכם אינו משנה כפי שמיקום קריאת `require` משנה. הייבוא הוא הצטרפות־מרצון לכל שם: `use List::Util qw(sum0 max)` מביא בדיוק את `sum0` ו־`max` אל מרחב השמות שלכם, ולא יותר. ## אובייקטים ל־Lua אין מערכת אובייקטים מובנית; אתם בונים אחת מ־tables ו־metatables - `setmetatable(t, {__index = Class})` לחיפוש מתודות ולהורשה, `__add`/`__eq` לאופרטורים. Perl 5.42 נותנת לכם תחביר `class` ממדרגה ראשונה במקום זאת, כך שהמקרה הנפוץ אינו זקוק לחיווט metatable. שדות מוצהרים עם `field`, מתודות עם `method`, ו־`$self` זמין בתוך כל מתודה (אינכם מעבירים אותו כפרמטר הראשון כפי שה־`self` / קריאת־הנקודתיים של Lua עושים). ```lua local Point = {} Point.__index = Point function Point.new(x, y) return setmetatable({x = x or 0, y = y or 0}, Point) end function Point:to_string() return "(" .. self.x .. ", " .. self.y .. ")" end function Point:move(dx, dy) self.x = self.x + dx self.y = self.y + dy return self end local p = Point.new(3, 4) print(p:to_string()) -- (3, 4) p:move(1, 1) print(p:to_string()) -- (4, 5) ``` ```perl use v5.42; use feature 'class'; class Point { field $x :param = 0; field $y :param = 0; method to_string { return "($x, $y)" } method move ($dx, $dy) { $x += $dx; $y += $dy; return $self; } } my $p = Point->new(x => 3, y => 4); say $p->to_string; # (3, 4) $p->move(1, 1); say $p->to_string; # (4, 5) ``` הבנאי נוצר עבורכם, מחליף את ה־`Point.new` הכתוב־ביד של Lua: `:param` מסמן שדה שה־`new` הבנוי־אוטומטית מקבל כארגומנט על שם, ו־`= 0` הוא ברירת המחדל שלו. הורשה, ש־Lua בונה משרשרת של metatables של `__index`, היא התכונה `:isa`: ```perl use v5.42; use feature 'class'; class Point3D :isa(Point) { field $z :param = 0; method z { return $z } } my $q = Point3D->new(x => 1, y => 2, z => 3); say $q->to_string, " z=", $q->z; # (1, 2) z=3 ``` המלכודת: תכונת ה־`class` עדיין מסומנת כניסיונית ב־Perl 5.42, ועל `pperl` הודעות ”class is experimental“ מודפסות אל **stderr** אפילו כשאתם מנסים להשתיק אותן. הן אינן נוגעות בפלט התוכנית שלכם, ולכן סקריפט המדפיס אל stdout מתנהג בדיוק כפי שמוצג למעלה; פשוט צפו להודעות בזרם השגיאות. מתודות־המטא של אופרטורים של Lua (`__add`, `__eq`, וחבריהם) ממופות אל הפרגמה `overload` של Perl ולא אל `class`; עבורן, עבור מודל האובייקטים הישן יותר מבוסס ה־`bless`, roles, ופרטי הורשה, ראו את [מדריך התכנות מונחה־העצמים](../oop/index.md). ## טיפול בשגיאות הקריאה המוגנת `pcall(f)` של Lua - המחזירה `false, err` במקום להיפרם - יש לה שתי מקבילות ב־Perl. הקלאסית עוטפת קוד ב־`eval { }` ובוחנת את `$@` (השגיאה שנתפסה) לאחר מכן: ```lua local ok, err = pcall(function() risky(-1) end) if not ok then print("caught: " .. err) end ``` ```perl use v5.42; my $result = eval { risky(-1) }; if ($@) { print "caught: $@"; # caught: negative! } ``` הצורה המודרנית היא `try`/`catch`, הנקראת יותר כמו בלוק חריגות מקובל מאשר ריקוד ההחזרה־הבוליאנית של `pcall`: ```perl use v5.42; use feature 'try'; try { risky(-4); } catch ($e) { print "caught: $e"; # caught: negative! } ``` כאשר הצד הזורק הוא `die`, ה־`error` של 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"` מצורף אוטומטית (ה־`error` של Lua עושה קידום־מיקום דומה אלא אם תעבירו רמה `0`). סיימו את מחרוזות השגיאה שלכם ב־`\n` כשאתם רוצים הודעה נקייה, והשמיטו זאת כשאתם רוצים את עקבת הקובץ־והשורה. שימו לב גם לשני משתני השגיאה: `$@` הוא החריגה האחרונה שנתפסה, בעוד `$!` הוא שגיאת ה *מערכת* האחרונה (`open` שנכשל, מחרוזת ה־`errno`). ## ניבים כמה דפוסים שתראו מדי יום ותרצו באצבעות שלכם. מיון רשימה לפי מפתח מחושב (טרנספורם שוורץ - קישוט, מיון, הסרת קישוט בצינור אחד, הרפלקס של Perl במקום ש־Lua מעבירה פונקציית משווה אל `table.sort`): ```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 מרשימה, ההופכת רצף לטבלת חיפוש בצעד אחד: ```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`, תפקיד ש־Lua אינה מכוונת אליו. שורת־היחיד עם הלולאה המובלעת היא הרפלקס היומיומי של מפתח Perl: ```bash pperl -lane '$s += $F[-1]; END { print $s }' data.txt ``` המלכודת: קראו את [מדריך שורות־היחיד](../oneliner/index.md) עבור המתגים (`-n`, `-p`, `-a`, `-l`, `-e`) ועשרות המתכונים הבנויים עליהם; זהו אחד הדברים בעלי המנוף הגבוה ביותר ללמוד בהגעה מרקע של תסריוט שבו הנישה הזו הייתה עבודתו של מישהו אחר. ## מלכודות בהגעה מ־Lua המלכודות, אסופות לסריקה אחרונה לפני שאתם מתחילים לכתוב: - **table יחיד הופך לשני טיפוסים.** table של Lua הוא `@array` (ממופתח לפי מספרים, מסודר) *או* `%hash` (ממופתח לפי מחרוזות, לא מסודר) ב־Perl, לעולם לא שניהם בבת אחת. החליטו איזה מהם יש לכם לפני שאתם פונים אליו. - **מערכים הם בסיס־0.** ה־`t[1]` של Lua הוא ה־`$t[0]` של Perl; האיבר האחרון הוא `$t[-1]`. זוהי שגיאת ה־off-by-one הנפוצה ביותר בהמרה. - **Sigils משתנים עם השימוש.** איבר אחד של `@a` הוא `$a[0]`; ערך אחד של `%h` הוא `$h{k}`. sigil המכל ו־sigil האיבר נבדלים - לשם ה־Lua החשוף `t` אין מעבר כזה. - **שרשור הוא `.`, לא `..`.** ה־`..` של Lua אינו קיים; `.` מצרף מחרוזות ו־`x` משכפל אותן. - **אמיתות הפוכה באפס.** ב־Lua ‏`0` ו־`""` הם *אמת*; ב־Perl הם **שקר**. רק `0`, `""`, `"0"`, ו־`undef` הם שקר, ולכן `"00"` ו־`"0.0"` הם באופן מפתיע **אמת**. - **שתי משפחות השוואה.** `==`/`<`/`<=>` למספרים, `eq`/`lt`/`cmp` למחרוזות. בחרו לפי טיפוס האופרנד; ל־`==` היחיד של Lua אין פיצול מקביל. - **`sort` ממוין כמחרוזות כברירת מחדל.** העבירו `{ $a <=> $b }` לסדר מספרי. - **הקשר.** `my $x = @a` הוא *האורך*; `my ($x) = @a` הוא *האיבר הראשון*. צורת הרשימה היא ידידכם `local a, b = f()`; צורת הסקלר חדשה. קראו מחדש את [הקשר]() כשערך חוזר בצורה הלא־נכונה. - **Regex אינו תבניות Lua.** ל־Perl יש regex מלא (`\d`, `|`, `*`, קבוצות על שם); תבניות Lua (`%d`, ללא חלופיות) הן שפה שונה וקטנה יותר. `s///` עורך במקום ומחזיר מניין; השתמשו ב־`/r` למחרוזת חדשה. - **`open` אינו זורק.** השתמשו ב־`open ... or die "...: $!"`, כפי שה־`io.open` של Lua מחזיר `nil, err`. - **הריצו `chomp` על קריאות השורה שלכם.** `<$fh>` שומר על תו השורה החדשה שבסוף שה־`lines()` של Lua היה מסיר. - **מקור שאינו 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. Operator metamethods map to the `overload` pragma, not to `class`.