מ־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:
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 אלא אם תאמרו אחרת.
local name = "Ada"
local langs = {"Lua", "Perl"}
local ages = {Ada = 36, Linus = 56}
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 משתמשת ב־...
local name = "Ada"
local greeting = "Hello, " .. name
local shout = string.rep("AB", 3)
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.
print(7 / 2) -- 3.5
print(math.floor(7 / 2)) -- 3
print(2 ^ 10) -- 1024.0
print(10 % 3) -- 1
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.
local t = {"x", "y", "z"}
print(t[1]) -- x (Lua: first element is index 1)
print(t[3]) -- z
print(#t) -- 3 (length)
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:
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 (סינון), עם $_ כאיבר הנוכחי:
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}.
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
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 מכלילה את הרעיון הזה לתכונה של כל ביטוי. כל ביטוי רץ או בהקשר רשימה או בהקשר סקלר, הנקבע לפי המקום שבו הוא מופיע. אותו מערך עצמו מתנהג אחרת בכל אחד מהם: בהשמה אל סקלר הוא נותן את אורכו, בהשמה אל רשימה (משתנים בסוגריים) הוא מעתיק איברים.
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; הקשר הקורא מחליט מה חוזר:
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 אינן יכולות לעשות:
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). כל השאר הוא אמת.
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
המלכודת: האינסטינקט של מפתח 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):
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, ובלוקים מוקפים בסוגריים מסולסלים במקום מתוחמים במילות מפתח.
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
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. משני סופית מאפשרים למשפט פשוט לשאת עליו את התנאי או הלולאה שלו עצמו, מה שנקרא בטבעיות עבור שורות־יחיד:
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.
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
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 אוספת:
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 אתם לוקחים הפניה במפורש עם \, ועוקבים אחריה עם ->.
local data = {name = "Ada", langs = {"Perl", "Lua"}}
print(data.langs[2]) -- Lua
table.insert(data.langs, "Ruby")
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 - אחד המעברים הנקיים ביותר בכל המדריך:
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, ולכידות על שם ב־%+.
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
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.gmatch:
use v5.42;
my @nums = "x1y2z3" =~ /(\d)/g; # 1 2 3
say "@nums";
המלכודת: התווים המטא נבדלים מתבניות Lua - Perl משתמשת ב־\d במקום ש־Lua משתמשת ב־%d, בכמתים */+/?, בחלופיות |, ובקבוצות על שם (?<name>...), שאף אחת מהן אינה נתמכת בתבניות 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.
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()
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> שומרת על תו השורה החדשה שבסופה (ה־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 מייבא שמות נבחרים ישירות אל מרחב השמות שלכם.
local sqrt = math.sqrt
local sum = 0
for i = 1, 10 do sum = sum + i end
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 עושים).
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)
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:
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, ופרטי הורשה, ראו את מדריך התכנות מונחה־העצמים.
טיפול בשגיאות#
הקריאה המוגנת pcall(f) של Lua - המחזירה false, err במקום להיפרם - יש לה שתי מקבילות ב־Perl. הקלאסית עוטפת קוד ב־eval { } ובוחנת את $@ (השגיאה שנתפסה) לאחר מכן:
local ok, err = pcall(function() risky(-1) end)
if not ok then
print("caught: " .. err)
end
use v5.42;
my $result = eval { risky(-1) };
if ($@) {
print "caught: $@"; # caught: negative!
}
הצורה המודרנית היא try/catch, הנקראת יותר כמו בלוק חריגות מקובל מאשר ריקוד ההחזרה־הבוליאנית של pcall:
use v5.42;
use feature 'try';
try {
risky(-4);
}
catch ($e) {
print "caught: $e"; # caught: negative!
}
כאשר הצד הזורק הוא die, ה־error של 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):
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 מרשימה, ההופכת רצף לטבלת חיפוש בצעד אחד:
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:
pperl -lane '$s += $F[-1]; END { print $s }' data.txt
המלכודת: קראו את מדריך שורות־היחיד עבור המתגים (-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
classfeature prints experimental notices to stderr onpperl. Correct output, noisy error stream. Operator metamethods map to theoverloadpragma, not toclass.