| PERLSYN(1) | دليل مرجع مبرمجي بيرل | PERLSYN(1) |
الاسم¶
perlsyn - صياغة بيرل: التصريحات، والعبارات، والملاحظات
الوصف¶
يتكون برنامج بيرل من تسلسل من التصريحات والعبارات التي تعمل من الأعلى إلى الأسفل. تسمح لك الحلقات، والبرامج الفرعية، وهياكل التحكم الأخرى بالتنقل داخل الكود.
بيرل لغة حرة التنسيق: يمكنك تنسيقها وإزاحتها كما تشاء. تخدم المسافات البيضاء غالبا في فصل الوحدات (tokens)، على عكس لغات مثل بايثون حيث تعد جزءا رئيسا من الصياغة، أو فورتران حيث لا تهم فيها المسافات.
العديد من العناصر النحوية في Perl تكون اختيارية. بدلًا من مطالبتك بوضع أقواس حول كل استدعاء دالة والتصريح عن كل متغير، يمكنك غالبًا ترك هذه العناصر الصريحة وسيفهم Perl ما تقصده. يُعرف هذا باسم افعل ما أعنيه، ويُختصر بـ DWIM. يتيح ذلك للمبرمجين أن يكونوا كسالى وأن يبرمجوا بالأسلوب الذي يريحهم.
تستعير بيرل الصياغة والمفاهيم من لغات عديدة: awk، و sed، و C، وصدفة Bourne، و Smalltalk، و Lisp وحتى الإنجليزية. وقد استعارت لغات أخرى الصياغة من بيرل، وخاصة توسعات التعبيرات النمطية. لذا إذا كنت قد برمجت بلغة أخرى فستجد أجزاء مألوفة في بيرل. غالبا ما تعمل بنفس الطريقة، ولكن راجع perltrap للحصول على معلومات حول كيفية اختلافها.
التصريحات¶
الأشياء الوحيدة التي تحتاج إلى التصريح عنها في Perl هي تنسيقات التقارير والروتينات الفرعية (وأحيانًا لا تحتاج حتى للروتينات الفرعية). يحمل المتغير العددي القيمة غير المعرفة ("undef") حتى تُسند إليه قيمة معرفة، وهي أي شيء بخلاف "undef". عند استخدامه كعدد، يُعامل "undef" كـ 0؛ وعند استخدامه كسلسلة نصية، يُعامل كالسلسلة الفارغة، ""؛ وعند استخدامه كمرجع لا يُسند إليه شيء، يُعامل كخطأ. إذا فعلت التحذيرات، فسيتم إخطارك بوجود قيمة غير مهيأة كلما عاملت "undef" كسلسلة نصية أو عدد. حسنًا، عادةً. السياقات المنطقية، مثل:
if ($x) {}
مستثناة من التحذيرات (لأنها تهتم بالصحة وليس بالتعريف). العوامل مثل "++"، و "--"، و "+="، و "-="، و ".="، التي تعمل على متغيرات غير معرفة مثل:
undef $x;
$x++;
مستثناة أيضا دائما من هذه التحذيرات.
يمكن وضع التصريح في أي مكان توضع فيه الجملة، ولكن ليس له أي تأثير على تنفيذ تسلسل الجمل الرئيس: كل التصريحات تدخل حيز التنفيذ في وقت التصريف. عادةً ما توضع كل التصريحات في بداية السكربت أو نهايته. ومع ذلك، إذا كنت تستخدم متغيرات خاصة ذات نطاق لغوي أُنشئت باستخدام my()، أو state()، أو our()، فسيتعين عليك التأكد من أن تعريف التنسيق أو الروتين الفرعي يقع ضمن نفس نطاق الكتلة الخاص بـ my() إذا كنت تتوقع التمكن من الوصول إلى تلك المتغيرات الخاصة.
يسمح التصريح عن برنامج فرعي باستخدام اسم البرنامج الفرعي كما لو كان عامل قائمة من تلك النقطة فصاعدا في البرنامج. يمكنك التصريح عن برنامج فرعي دون تعريفه بقول "sub name"، هكذا:
sub myname;
$me = myname $0 or die "can't get myname";
تصريح مجرد مثل هذا يصرح عن الدالة لتكون عامل قائمة، وليس عاملا أحاديا، لذا عليك توخي الحذر في استخدام الأقواس (أو "or" بدلا من "||"). يرتبط العامل "||" بقوة أكبر من أن يُستخدم بعد عوامل القائمة؛ إذ يصبح جزءا من العنصر الأخير. يمكنك دائما استخدام الأقواس حول وسائط عوامل القائمة لتحويل عامل القائمة مرة أخرى إلى شيء يتصرف بشكل أقرب إلى استدعاء دالة. وبدلا من ذلك، يمكنك استخدام النموذج الأولي "($)" لتحويل البرنامج الفرعي إلى عامل أحادي:
sub myname ($); $me = myname $0 || die "can't get myname";
يُحلل ذلك الآن كما تتوقع، ولكن لا يزال يتعين عليك التعود على استخدام الأقواس في تلك الحالة. لمزيد من المعلومات حول النماذج الأولية، راجع perlsub.
يمكن أيضا تحميل تصريحات البرامج الفرعية باستخدام عبارة "require" أو تحميلها واستيرادها في مساحة الأسماء الخاصة بك باستخدام عبارة "use". راجع perlmod للحصول على تفاصيل حول هذا الموضوع.
قد يحتوي تسلسل العبارات على تصريحات لمتغيرات ذات نطاق معجمي، ولكن بصرف النظر عن التصريح عن اسم متغير، فإن التصريح يعمل كعبارة عادية، ويُفصل ضمن تسلسل العبارات كما لو كان عبارة عادية. وهذا يعني أن له آثارا في كل من وقت التصريف ووقت التشغيل.
الملاحظات¶
النص من محرف "#" حتى نهاية السطر هو ملاحظة، ويتم تجاهله. تشمل الاستثناءات "#" داخل سلسلة نصية أو تعبير نمطي.
العبارات البسيطة¶
النوع الوحيد من الجمل البسيطة هو التعبير الذي تُحسب قيمته من أجل آثاره الجانبية. يجب إنهاء كل جملة بسيطة بفاصلة منقوطة، ما لم تكن الجملة الأخيرة في كتلة، وفي هذه الحالة تكون الفاصلة المنقوطة اختيارية. ولكن ضع الفاصلة المنقوطة على أي حال إذا كانت الكتلة تشغل أكثر من سطر واحد، لأنك قد تضيف سطرًا آخر في النهاية. لاحظ أن هناك عوامل مثل "eval {}" و "sub {}" و "do {}" تبدو مثل الجمل المركبة، لكنها ليست كذلك -- فهي مجرد حدود (TERMs) في تعبير -- وبالتالي تحتاج إلى إنهاء صريح عند استخدامها كآخر عنصر في الجملة.
معدلات العبارة¶
يمكن اختياريا أن تتبع أي عبارة بسيطة بمعدل واحد فقط، مباشرة قبل الفاصلة المنقوطة النهائية (أو نهاية الكتلة). المعدلات الممكنة هي:
if EXPR
unless EXPR
while EXPR
until EXPR
for LIST
foreach LIST
when EXPR
يُشار إلى الـ "EXPR" التي تلي المعدل باسم "الشرط". يحدد صدقه أو كذبه كيفية سلوك المعدل.
ينفذ "if" العبارة مرة واحدة إذا وفقط إذا كان الشرط صحيحا. "unless" هو العكس، حيث ينفذ العبارة ما لم يكن الشرط صحيحا (أي إذا كان الشرط خاطئا). راجع "القيم القياسية" في perldata للتعرف على تعريفات الصحيح والخاطئ.
print "Basset hounds got long ears" if length $ear >= 10;
go_outside() and play() unless $is_raining;
المعدل for(each) هو مكرر (iterator): فهو ينفذ العبارة مرة واحدة لكل عنصر في الـ LIST (مع ربط $_ بكل عنصر بالدور). لا توجد صياغة لتحديد حلقة for بنمط C أو متغير تكرار ذو نطاق معجمي في هذا النموذج.
print "Hello $_!\n" for qw(world Dolly nurse);
يكرر "while" العبارة بينما يكون الشرط صحيحا. يمتلك "while" اللاحق نفس المعاملة السحرية لبعض أنواع الشروط التي يمتلكها "while" البادئ. يقوم "until" بالعكس، حيث يكرر العبارة حتى يصبح الشرط صحيحا (أو بينما يكون الشرط خاطئا):
# كلاهما يعد من 0 إلى 10.
print $i++ while $i <= 10;
print $j++ until $j > 10;
لمعدلي "while" و "until" معاني "حلقة "while"" المعتادة (يُقيم الشرط أولا)، إلا عند تطبيقهما على "do"-BLOCK (أو على عبارة "do"-SUBROUTINE في بيرل 4)، وفي هذه الحالة تُنفذ الكتلة مرة واحدة قبل تقييم الشرط.
هذا لكي تتمكن من كتابة حلقات مثل:
do {
$line = <STDIN>;
...
} until !defined($line) || $line eq ".\n"
راجع "do" في perlfunc. لاحظ أيضًا أن جمل التحكم في الحلقة الموصوفة لاحقًا لن تعمل في هذا التركيب، لأن المعدلات لا تقبل لصائق الحلقات. عذرًا. يمكنك دائمًا وضع كتلة أخرى بداخلها (من أجل "next"/"redo") أو حولها (من أجل "last") للقيام بهذا النوع من الأشياء.
لـ "next" أو "redo"، فقط ضاعف الأقواس:
do {{
next if $x == $y;
# افعل شيئا هنا
}} until $x++ > $z;
بالنسبة لـ "last"، عليك أن تكون أكثر تفصيلا وتضع أقواسا حولها:
{
do {
last if $x == $y**2;
# افعل شيئا هنا
} while $x++ <= $z;
}
إذا كنت بحاجة إلى كل من "next" و "last"، فعليك القيام بكليهما واستخدام لصيقة حلقة أيضا:
LOOP: {
do {{
next if $x == $y;
last LOOP if $x == $y**2;
# افعل شيئا هنا
}} until $x++ > $z;
}
ملاحظة: سلوك "my" أو "state" أو "our" المعدل بمعدل جملة شرطي أو تركيب حلقة (على سبيل المثال، "my $x if ...") هو غير محدد. قد تكون قيمة متغير "my" هي "undef"، أو أي قيمة أُسندت سابقًا، أو ربما أي شيء آخر. لا تعتمد عليه. قد تقوم الإصدارات المستقبلية من perl بشيء مختلف عن إصدار perl الذي تجربه عليه. احذر المخاطر.
معدل "when" هو ميزة تجريبية ظهرت لأول مرة في بيرل 5.14. لاستخدامه، يجب عليك تضمين تصريح "use v5.14". (من الناحية التقنية، يتطلب فقط ميزة "switch"، ولكن هذا الجانب منها لم يكن متاحا قبل 5.14). وهو يعمل فقط من داخل حلقة "foreach" أو كتلة "given"، حيث ينفذ العبارة فقط إذا كان المطابق الذكي "$_ ~~ EXPR" صحيحا. إذا نُفذت العبارة، فسيتبعها "next" من داخل "foreach" و "break" من داخل "given".
في ظل التنفيذ الحالي، يمكن أن تكون حلقة "foreach" في أي مكان ضمن النطاق الديناميكي لمعدل "when"، ولكن يجب أن تكون ضمن النطاق اللغوي لكتلة "given". قد يتم تخفيف هذا القيد في إصدار مستقبلي. راجع "Switch Statements" أدناه.
العبارات المركبة¶
في بيرل، يُطلق على تسلسل العبارات الذي يحدد نطاقا اسم كتلة. أحيانا يتم تحديد الكتلة بواسطة الملف الذي يحتوي عليها (في حالة ملف مطلوب، أو البرنامج ككل)، وأحيانا يتم تحديد الكتلة بمدى سلسلة نصية (في حالة eval).
ولكن بشكل عام، يتم تحديد الكتلة بواسطة أقواس معقوفة. سنسمي هذا البناء الصوري BLOCK. نظرا لأن الأقواس المحيطة هي أيضا صياغة لتعبيرات منشئ مرجع الهاش (راجع perlref)، فقد تحتاج أحيانا إلى إزالة اللبس بوضع ";" مباشرة بعد قوس الفتح حتى تدرك بيرل أن القوس هو بداية كتلة. ستحتاج في كثير من الأحيان إلى إزالة اللبس بالطريقة الأخرى، عن طريق وضع "+" مباشرة قبل قوس الفتح لإجباره على تفسيره كتعبير منشئ مرجع هاش. يُعتبر من الأسلوب الجيد استخدام آليات إزالة اللبس هذه بكثرة، وليس فقط عندما تخمن بيرل بشكل غير صحيح.
يمكن استخدام العبارات المركبة التالية للتحكم في التدفق:
if (EXPR) BLOCK
if (EXPR) BLOCK else BLOCK
if (EXPR) BLOCK elsif (EXPR) BLOCK ...
if (EXPR) BLOCK elsif (EXPR) BLOCK ... else BLOCK
unless (EXPR) BLOCK
unless (EXPR) BLOCK else BLOCK
unless (EXPR) BLOCK elsif (EXPR) BLOCK ...
unless (EXPR) BLOCK elsif (EXPR) BLOCK ... else BLOCK
given (EXPR) BLOCK
LABEL while (EXPR) BLOCK
LABEL while (EXPR) BLOCK continue BLOCK
LABEL until (EXPR) BLOCK
LABEL until (EXPR) BLOCK continue BLOCK
LABEL for (EXPR; EXPR; EXPR) BLOCK
LABEL for VAR (LIST) BLOCK
LABEL for VAR (LIST) BLOCK continue BLOCK
LABEL foreach (EXPR; EXPR; EXPR) BLOCK
LABEL foreach VAR (LIST) BLOCK
LABEL foreach VAR (LIST) BLOCK continue BLOCK
LABEL BLOCK
LABEL BLOCK continue BLOCK
PHASE BLOCK
بدءا من بيرل 5.36، يمكنك التكرار عبر قيم متعددة في وقت واحد عن طريق تحديد قائمة من المتغيرات المعجمية داخل أقواس:
LABEL for my (VAR, VAR) (LIST) BLOCK
LABEL for my (VAR, VAR) (LIST) BLOCK continue BLOCK
LABEL foreach my (VAR, VAR) (LIST) BLOCK
LABEL foreach my (VAR, VAR) (LIST) BLOCK continue BLOCK
إذا فُعلت ميزة "try"، فيمكن أيضا استخدام ما يلي
try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK
عبارة "given" التجريبية لا تُفعل آليا؛ راجع "عبارات التبديل" أدناه لمعرفة كيفية القيام بذلك، والتحذيرات المصاحبة لها.
على عكس لغتي C و Pascal، يتم تعريف كل هذه في بيرل من حيث الـ BLOCKs، وليس العبارات. وهذا يعني أن الأقواس المعقوفة مطلوبة - لا يُسمح بالعبارات المعلقة. إذا كنت تريد كتابة شروط بدون أقواس معقوفة، فهناك عدة طرق أخرى للقيام بذلك. كل ما يلي يفعل الشيء نفسه:
if (!open(FOO)) { die "Can't open $FOO: $!" }
die "Can't open $FOO: $!" unless open(FOO);
open(FOO) || die "Can't open $FOO: $!";
open(FOO) ? () : die "Can't open $FOO: $!";
# الأخير غريب بعض الشيء
جملة "if" واضحة ومباشرة. نظرًا لأن الكتل (BLOCKs) محاطة دائمًا بأقواس متعرجة، فلا يوجد أبدًا أي غموض حول أي "if" تتبعها "else". إذا استخدمت "unless" بدلاً من "if"، فسيتم عكس منطق الاختبار. مثل "if"، يمكن أن تتبع "unless" جملة "else". بل ويمكن أن تتبع "unless" جملة واحدة أو أكثر من جمل "elsif"، على الرغم من أنك قد ترغب في التفكير مرتين قبل استخدام هذا التركيب اللغوي المعين، حيث سيتعين على كل من يقرأ شفرتك التفكير مرتين على الأقل قبل أن يتمكنوا من فهم ما يحدث.
تنفيذ جملة "while" الكتلة طالما كان التعبير صحيحًا. جملة "until" تنفذ الكتلة طالما كان التعبير خاطئًا. الـ LABEL اختياري، وإذا وُجد، فإنه يتكون من معرف متبوع بنقطتين. يحدد LABEL الحلقة لجمل التحكم في الحلقة "next" و "last" و "redo". إذا حُذف LABEL، فإن جملة التحكم في الحلقة تشير إلى الحلقة المحيطة الأعمق. قد يتضمن ذلك البحث ديناميكيًا عبر مكدس الاستدعاءات الخاص بك في وقت التشغيل للعثور على LABEL. يطلق هذا السلوك اليائس تحذيرًا إذا كنت تستخدم برامج "use warnings" أو علم -w. إذا حدث أكثر من وسم بنفس الاسم، فإن أي إشارة إلى ذلك الاسم تشير إلى الوسم الذي يميز الحلقة المحيطة الأعمق.
إذا كان تعبير الشرط في جملة "while" يعتمد على أي من مجموعة من أنواع التعبيرات التكرارية، فإنه يحصل على بعض المعاملة السحرية. أنواع التعبيرات التكرارية المتأثرة هي "readline"، وعامل الإدخال "<FILEHANDLE>"، و "readdir"، و "glob"، وعامل التحجيم "<PATTERN>"، و "each". إذا كان تعبير الشرط أحد أنواع التعبيرات هذه، فسيتم إسناد القيمة التي ينتجها العامل التكراري ضمنيًا إلى $_. إذا كان تعبير الشرط أحد أنواع التعبيرات هذه أو إسنادًا صريحًا لأحدها إلى مقدار عددي، فإن الشرط يختبر في الواقع تعريف قيمة التعبير، وليس قيمته المنطقية العادية.
إذا كان هناك "continue" BLOCK، فإنه يُنفذ دائما قبل تقييم الشرط مرة أخرى مباشرة. وبالتالي يمكن استخدامه لزيادة متغير الحلقة، حتى عندما تستمر الحلقة عبر عبارة "next".
عندما تُسبق الكتلة بكلمة مفتاحية لمرحلة التصريف مثل "BEGIN"، أو "END"، أو "INIT"، أو "CHECK"، أو "UNITCHECK"، فستعمل الكتلة فقط خلال مرحلة التنفيذ المقابلة. راجع perlmod لمزيد من التفاصيل.
يمكن للوحدات الامتدادية أيضًا الارتباط بمحلل Perl لتعريف أنواع جديدة من الجمل المركبة. يتم تقديم هذه عن طريق كلمة مفتاحية تتعرف عليها الوحدة الامتدادية، ويتم تعريف بناء الجملة الذي يلي الكلمة المفتاحية بالكامل بواسطة الامتداد. إذا كنت مطورًا، فراجع "PL_keyword_plugin" في perlapi لمعرفة الآلية. إذا كنت تستخدم مثل هذه الوحدة، فراجع وثائق الوحدة للحصول على تفاصيل حول بناء الجملة الذي تعرفه.
التحكم في الحلقة¶
يبدأ أمر "next" التكرار التالي للحلقة:
LINE: while (<STDIN>) {
next LINE if /^#/; # تجاهل الملاحظات
...
}
يخرج أمر "last" فورا من الحلقة المعنية. ولا تُنفذ كتلة "continue"، إن وجدت:
LINE: while (<STDIN>) {
last LINE if /^$/; # اخرج عند الانتهاء من الترويسة
...
}
يعيد أمر "redo" تشغيل كتلة الحلقة دون تقييم الشرط مرة أخرى. ولا تُنفذ كتلة "continue"، إن وجدت. يُستخدم هذا الأمر عادة من قبل البرامج التي تريد الكذب على نفسها بشأن ما تم إدخاله للتو.
على سبيل المثال، عند معالجة ملف مثل /etc/termcap. إذا كانت خطوط الإدخال الخاصة بك قد تنتهي بصلات مائلة خلفية للإشارة إلى الاستمرار، فأنت تريد القفز للأمام والحصول على السجل التالي.
while (<>) {
chomp;
if (s/\\$//) {
$_ .= <>;
redo unless eof();
}
# عالج $_ الآن
}
وهو اختصار بيرل للنسخة المكتوبة بشكل أكثر صراحة:
LINE: while (defined($line = <ARGV>)) {
chomp($line);
if ($line =~ s/\\$//) {
$line .= <ARGV>;
redo LINE unless eof(); # ليس eof(ARGV)!
}
# عالج $line الآن
}
لاحظ أنه لو كانت هناك كتلة "continue" في الكود أعلاه، فسيتم تنفيذها فقط على الأسطر التي تم تجاهلها بواسطة التعبير النمطي (بما أن redo يتخطى كتلة التكملة). غالبا ما تُستخدم كتلة التكملة لإعادة ضبط عدادات الأسطر أو تطابقات "m?pat?" لمرة واحدة:
# مستوحى من :1,$g/fred/s//WILMA/
while (<>) {
m?(fred)? && s//WILMA $1 WILMA/;
m?(barney)? && s//BETTY $1 BETTY/;
m?(homer)? && s//MARGE $1 MARGE/;
} continue {
print "$ARGV $.: $_";
close ARGV if eof; # رستر $.
reset if eof; # رستر ?pat?
}
إذا استُبدلت كلمة "while" بكلمة "until"، فسيتم عكس معنى الاختبار، ولكن لا يزال يتم اختبار الشرط قبل التكرار الأول.
لا تعمل جمل التحكم في الحلقة في "if" أو "unless"، لأنها ليست حلقات تكرار. يمكنك مضاعفة الأقواس المتعرجة لجعلها كذلك، مع ذلك.
if (/pattern/) {{
last if /fred/;
next if /barney/; # له نفس تأثير "last"،
# لكنه لا يوفر توثيقًا جيدًا
# افعل شيئًا هنا
}}
يعود هذا إلى حقيقة أن الكتلة (block) بحد ذاتها تعمل كحلقة تكرارية تنفذ مرة واحدة، انظر "Basic BLOCKs".
الصيغة "while/if BLOCK BLOCK"، التي كانت متاحة في Perl 4، لم تعد متوفرة. استبدل أي ظهور لـ "if BLOCK" بـ "if (do BLOCK)".
حلقات For¶
حلقة "for" بأسلوب C في Perl تعمل مثل حلقة "while" المقابلة؛ وهذا يعني أن هذا:
for ($i = 1; $i < 10; $i++) {
...
}
يكافئ هذا:
$i = 1;
while ($i < 10) {
...
} continue {
$i++;
}
هناك فرق بسيط واحد: إذا صُرّح عن المتغيرات باستخدام "my" في قسم التهيئة الخاص بـ "for"، فإن النطاق المعجمي (lexical scope) لهذه المتغيرات هو حلقة "for" تحديدًا (جسم الحلقة وأقسام التحكم). للتوضيح:
my $i = 'samba';
for (my $i = 1; $i <= 4; $i++) {
print "$i\n";
}
print "$i\n";
عند التنفيذ، يعطي:
1
2
3
4
samba
كحالة خاصة، إذا كان الاختبار في حلقة "for" (أو حلقة "while" المقابلة) فارغًا، فإنه يُعامل كقيمة صواب. أي أن كلاً من
for (;;) {
...
}
و
while () {
...
}
تُعاملان كحلقات لا نهائية.
بالإضافة إلى تكرار فهارس المصفوفات العادي، يمكن لـ "for" أن تصلح للعديد من التطبيقات المثيرة للاهتمام الأخرى. إليك واحدًا يتجنب المشكلة التي تقع فيها إذا قمت باختبار نهاية الملف بشكل صريح على واصف ملف تفاعلي مما يتسبب في ظهور برنامجك وكأنه متوقف.
$on_a_tty = -t STDIN && -t STDOUT;
sub prompt { print "yes? " if $on_a_tty }
for ( prompt(); <STDIN>; prompt() ) {
# افعل شيئًا
}
تحصل تعبيرات الشرط في حلقة "for" على نفس المعالجة السحرية لـ "readline" وأمثالها التي يحصل عليها تعبير الشرط في حلقة "while".
حلقات Foreach¶
تكرر حلقة "foreach" عبر قيمة قائمة عادية وتضبط المتغير العددي VAR ليكون كل عنصر من عناصر القائمة بالدور. إذا سُبق المتغير بالكلمة المفتاحية "my"، فإنه يكون ذا نطاق لغوي، وبالتالي لا يكون مرئيًا إلا داخل الحلقة. بخلاف ذلك، يكون المتغير محليًا ضمنيًا للحلقة ويستعيد قيمته السابقة عند الخروج من الحلقة. إذا تم التصريح عن المتغير مسبقًا باستخدام "my"، فإنه يستخدم ذلك المتغير بدلاً من المتغير العام، لكنه لا يزال محليًا للحلقة. يحدث هذا التوطين الضمني فقط للحلقات التي ليست بأسلوب C.
الكلمة المفتاحية "foreach" هي في الواقع مرادف للكلمة المفتاحية "for"، لذا يمكنك استخدام أيهما. إذا حُذف VAR، يُضبط $_ لكل قيمة.
إذا كان أي عنصر من عناصر القائمة (LIST) يمثل قيمة يسارية (lvalue)، فيمكنك تعديله عن طريق تعديل VAR داخل الحلقة. وعلى العكس، إذا لم يكن أي عنصر من عناصر LIST قيمة يسارية، فإن أي محاولة لتعديل ذلك العنصر ستفشل. بمعنى آخر، متغير فهرس حلقة "foreach" هو اسم مستعار ضمني لكل عنصر في القائمة التي تكرر عبرها.
إذا كان أي جزء من LIST مصفوفة، فإن "foreach" ستصبح مرتبكة للغاية إذا أضفت أو أزلت عناصر داخل جسم الحلقة، على سبيل المثال باستخدام "splice". لذا لا تفعل ذلك.
"foreach" ربما لن تفعل ما تتوقعه إذا كان VAR متغيرًا مربوطًا أو متغيرًا خاصًا آخر. لا تفعل ذلك أيضًا.
بدءًا من Perl 5.22، هناك متغير تجريبي لهذه الحلقة يقبل متغيرًا مسبوقًا بشرطة مائلة عكسية لـ VAR، وفي هذه الحالة يجب أن تكون العناصر في LIST مراجع. سيصبح المتغير المسبوق بشرطة مائلة عكسية اسمًا مستعارًا لكل عنصر مرجعي في LIST، والذي يجب أن يكون من النوع الصحيح. لا يلزم أن يكون المتغير مقدارًا عدديًا في هذه الحالة، وقد تتبع الشرطة المائلة العكسية "my". لاستخدام هذا النموذج، يجب عليك تمكين ميزة "refaliasing" عبر "use feature". (انظر الميزة. انظر أيضًا "Assigning to References" في perlref.)
بدءًا من Perl 5.36، يمكنك التكرار عبر قيم متعددة في المرة الواحدة. يمكنك التكرار فقط باستخدام مقادير عددية لغوية كمتغيرات تكرار - على عكس إسناد القائمة، ليس من الممكن استخدام "undef" للإشارة إلى قيمة غير مرغوب فيها. هذا قيد في التنفيذ الحالي، وقد يتغير في المستقبل.
إذا لم يكن حجم LIST مضاعفًا دقيقًا لعدد متغيرات التكرار، ففي التكرار الأخير ستكون متغيرات التكرار "الزائدة" أسماءً مستعارة لـ "undef"، كما لو كان قد أُلحق بـ LIST ", undef" لعدد المرات المطلوب حتى يصبح طولها مضاعفًا دقيقًا. يحدث هذا سواء كانت LIST قائمة حرفية أو مصفوفة - أي أن المصفوفات لا تُمدد إذا لم يكن حجمها مضاعفًا لحجم التكرار، بما يتوافق مع تكرار مصفوفة عنصرًا تلو الآخر. وبما أن عناصر الحشو هذه ليست قيمًا يسارية (lvalues)، فإن محاولة تعديلها ستفشل، بما يتوافق مع السلوك عند تكرار قائمة تحتوي على "undef" حرفية. إذا لم يكن هذا هو السلوك الذي تريده، فقبل بدء الحلقة إما أن تمدد مصفوفتك صراحة لتكون مضاعفًا دقيقًا، أو تطلق استثناءً صراحة.
أمثلة:
for (@ary) { s/foo/bar/ }
for my $elem (@elements) {
$elem *= 2;
}
for $count (reverse(1..10), "BOOM") {
print $count, "\n";
sleep(1);
}
for (1..15) { print "Merry Christmas\n"; }
foreach $item (split(/:[\\\n:]*/, $ENV{TERMCAP})) {
print "Item: $item\n";
}
use feature "refaliasing";
no warnings "experimental::refaliasing";
foreach \my %hash (@array_of_hash_references) {
# افعل شيئًا مع كل %hash
}
foreach my ($foo, $bar, $baz) (@list) {
# افعل شيئًا ثلاثة بثلاثة
}
foreach my ($key, $value) (%hash) {
# التكرار عبر الهاش
# يُنسخ الهاش فورًا إلى قائمة مسطحة قبل بدء الحلقة.
# تحتوي القائمة على نسخ من المفاتيح ولكن أسماء مستعارة للقيم.
# هذا هو نفس سلوك $var (%hash) {...}
}
إليك كيف قد يقوم مبرمج C بكتابة خوارزمية معينة في Perl:
for (my $i = 0; $i < @ary1; $i++) {
for (my $j = 0; $j < @ary2; $j++) {
if ($ary1[$i] > $ary2[$j]) {
last; # لا يمكن الذهاب للحلقة الخارجية :-(
}
$ary1[$i] += $ary2[$j];
}
# هذا هو المكان الذي يأخذني إليه last
}
بينما إليك كيف قد يفعل ذلك مبرمج Perl الأكثر دراية بأسلوب اللغة:
OUTER: for my $wid (@ary1) {
INNER: for my $jet (@ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
أرأيت كم هو أسهل؟ إنه أنظف، وأكثر أمانًا، وأسرع. إنه أنظف لأنه أقل ضجيجًا. وهو أكثر أمانًا لأنه إذا أُضيفت شفرة بين الحلقات الداخلية والخارجية لاحقًا، فلن تُنفذ الشفرة الجديدة عرضًا. تقوم "next" بتكرار الحلقة الأخرى صراحة بدلاً من مجرد إنهاء الحلقة الداخلية. وهي أسرع لأن Perl ينفذ جملة "foreach" بسرعة أكبر مما يفعل مع حلقة "for" المكافئة بأسلوب C.
ربما لاحظ مخترقو بيرل النبيهون أن حلقة "for" لها قيمة إرجاع، وأنه يمكن التقاط هذه القيمة عن طريق تغليف الحلقة في كتلة "do". المكافأة على هذا الاكتشاف هي هذه النصيحة التحذيرية: قيمة إرجاع حلقة "for" غير محددة وقد تتغير دون إشعار. لا تعتمد عليها.
معالجة الاستثناءات Try Catch¶
توفر صيغة "try"/"catch" تدفق تحكم يتعلق بمعالجة الاستثناءات. تقدم الكلمة المفتاحية "try" كتلة ستُنفذ عند مواجهتها، وتوفر كتلة "catch" كودًا لمعالجة أي استثناء قد يُطلق من الكتلة الأولى.
يجب تفعيل هذه الصيغة أولاً باستخدام "use feature 'try'".
use feature 'try';
try {
my $x = call_a_function();
$x < 100 or die "Too big";
send_output($x);
}
catch ($e) {
warn "Unable to output a value; $e";
}
print "Finished\n";
هنا، سيُنفذ جسم كتلة "catch" (أي جملة "warn") إذا استدعت الكتلة الأولية جملة "die" الشرطية، أو إذا أطلقت أي من الدوال التي تستدعيها استثناءً غير ملتقط. يمكن لكتلة "catch" فحص المتغير المعجمي $e في هذه الحالة لمعرفة ماهية الاستثناء. إذا لم يُطلق أي استثناء، فلن يتم تنفيذ كتلة "catch". وفي كلتا الحالتين، سيستمر التنفيذ من الجملة التالية - في هذا المثال جملة "print".
يجب أن تتبع الكلمة المفتاحية "catch" مباشرة بتصريح عن متغير بين قوسين، مما يقدم متغيرًا جديدًا مرئيًا لجسم الكتلة اللاحقة. داخل الكتلة، سيحتوي هذا المتغير على قيمة الاستثناء التي أُطلقت بواسطة الكود في كتلة "try". ليس من الضروري استخدام الكلمة المفتاحية "my" للتصريح عن هذا المتغير؛ فهذا أمر ضمني (بشكل مشابه لتواقيع الدوال الفرعية).
يُسمح لكل من كتلتي "try" و "catch" باحتواء تعبيرات تدفق التحكم، مثل "return"، أو "goto"، أو "next"/"last"/"redo". وفي جميع الحالات ستعمل كما هو متوقع دون تحذيرات. وبوجه خاص، فإن تعبير "return" داخل كتلة "try" سيجعل الدالة المحتوية له بالكامل تعود (return) - وهذا على عكس سلوكه داخل كتلة "eval"، حيث سيجعل تلك الكتلة فقط تعود.
مثل صيغ تدفق التحكم الأخرى، ستنتج "try" و "catch" آخر قيمة تم تقييمها عند وضعها كجملة أخيرة في دالة أو كتلة "do". وهذا يسمح باستخدام هذه الصيغة لإنشاء قيمة. في هذه الحالة تذكر عدم استخدام تعبير "return"، وإلا سيؤدي ذلك إلى عودة الدالة المحتوية.
my $value = do {
try {
get_thing(@args);
}
catch ($e) {
warn "Unable to get thing - $e";
$DEFAULT_THING;
}
};
كما هو الحال مع صيغ تدفق التحكم الأخرى، لا تظهر كتل "try" لـ caller() (تمامًا كما لا تظهر حلقات "while" أو "foreach" على سبيل المثال). يمكن للمستويات المتتالية من نتيجة caller() رؤية استدعاءات الدوال الفرعية وكتل "eval"، لأن تلك تؤثر على الطريقة التي تعمل بها "return". وبما أن كتل "try" لا تعترض "return"، فهي ليست ذات أهمية للمستدعي caller().
يمكن اختياريًا أن تتبع كتلتي "try" و "catch" بكتلة ثالثة تقدمها الكلمة المفتاحية "finally". تُنفذ هذه الكتلة الثالثة بعد انتهاء بقية البنية.
try {
call_a_function();
}
catch ($e) {
warn "Unable to call; $e";
}
finally {
print "Finished\n";
}
كتلة "finally" مكافئة لاستخدام كتلة "defer" وسيتم استدعاؤها في نفس المواقف؛ سواء اكتملت كتلة "try" بنجاح، أو أطلقت استثناءً، أو نقلت التحكم إلى مكان آخر باستخدام "return"، أو أداة تحكم في الحلقة، أو "goto".
على عكس كتلتي "try" و "catch"، لا يُسمح لكتلة "finally" بـ "return" أو "goto" أو استخدام أي أدوات تحكم في الحلقات. يتم تجاهل قيمة التعبير النهائي، ولا تؤثر على قيمة إرجاع الدالة المحتوية حتى لو وُضعت في نهاية الدالة.
استخدام صيغة كتلة "finally" هذه تجريبي حاليًا وسيصدر تحذيرًا في فئة "experimental::try".
الكتل (BLOCKs) الأساسية¶
تكون الـ BLOCK بمفردها (سواء كانت موسومة أم لا) مكافئة دلاليًا لحلقة تُنفذ مرة واحدة. وبالتالي يمكنك استخدام أي من جمل التحكم في الحلقة فيها لمغادرة الكتلة أو إعادة تشغيلها. (لاحظ أن هذا ليس صحيحًا في كتل "eval{}"، أو "sub{}"، أو خلافًا للاعتقاد الشائع، كتل "do{}"، والتي لا تُعد حلقات.) كتلة "continue" اختيارية.
يمكن استخدام بنية BLOCK لمحاكاة بنيات الاختيار (case structures).
SWITCH: {
if (/^abc/) { $abc = 1; last SWITCH; }
if (/^def/) { $def = 1; last SWITCH; }
if (/^xyz/) { $xyz = 1; last SWITCH; }
$nothing = 1;
}
ستجد أيضًا حلقة "foreach" تلك تُستخدم لإنشاء موضوعي (topicalizer) ومبدل (switch):
SWITCH:
for ($var) {
if (/^abc/) { $abc = 1; last SWITCH; }
if (/^def/) { $def = 1; last SWITCH; }
if (/^xyz/) { $xyz = 1; last SWITCH; }
$nothing = 1;
}
تُستخدم مثل هذه البنيات بشكل متكرر، لأن الإصدارات القديمة من بيرل لم تكن تحتوي على جملة "switch" رسمية، وأيضًا لأن الإصدار الجديد الموصوف أدناه مباشرة لا يزال تجريبيًا ويمكن أن يكون مربكًا أحيانًا.
كتل defer¶
توفر الكتلة المسبوقة بالمعدل "defer" قسمًا من الكود الذي يعمل في وقت لاحق أثناء الخروج من النطاق.
يمكن أن تظهر كتلة "defer" في أي نقطة يُسمح فيها بكتلة عادية أو جملة أخرى. إذا وصل تدفق التنفيذ إلى هذه الجملة، يتم تخزين جسم الكتلة لوقت لاحق، ولكن لا يتم استدعاؤه فورًا. عندما يغادر تدفق التحكم الكتلة المحتوية لأي سبب، تُنفذ هذه الكتلة المخزنة أثناء الخروج. إنها توفر وسيلة لتأجيل التنفيذ حتى وقت لاحق. هذا يعمل بشكل مشابه للصيغ التي توفرها بعض اللغات الأخرى، وغالبًا ما تستخدم كلمات مفتاحية تسمى "try / finally".
هذه الصيغة متاحة منذ Perl 5.36 إذا فُعلت بواسطة ميزة "defer" المسماة، وهي تجريبية حاليًا. إذا فُعلت التحذيرات التجريبية، فسيصدر تحذير عند استخدامها.
use feature 'defer';
{
say "يحدث هذا أولاً";
defer { say "يحدث هذا آخراً"; }
say "وهذا يحدث بينهما";
}
إذا كانت هناك عدة كتل "defer" محتواة في نطاق واحد، فإنها تُنفذ بترتيب LIFO (آخر من يدخل يخرج أولاً)؛ أي أن آخر كتلة يتم الوصول إليها هي أول كتلة يتم تنفيذها.
سيتم استدعاء الكود المخزن بواسطة كتلة "defer" عندما يغادر التحكم الكتلة المحتوية له بسبب المرور العادي، أو "return" صريح، أو استثناءات أُطلقت بواسطة "die" أو نُشرت بواسطة الدوال التي تستدعيها، أو "goto"، أو أي من جمل التحكم في الحلقات "next"، أو "last"، أو "redo".
إذا لم يصل تدفق التحكم إلى جملة "defer" نفسها، فلن يتم تخزين جسمها للتنفيذ اللاحق. (هذا في تضاد مباشر مع الكود الذي توفره كتلة "END"، والتي يضعها المترجم دائمًا في الطابور، بغض النظر عما إذا كان التنفيذ قد وصل إلى السطر الذي وردت فيه أم لا).
use feature 'defer';
{
defer { say "هذا سيعمل"; }
return;
defer { say "هذا لن يعمل"; }
}
الاستثناءات التي يُطلقها الكود داخل كتلة "defer" ستنتشر إلى المستدعي بنفس الطريقة التي ينتشر بها أي استثناء آخر يطلقه الكود العادي.
إذا كانت كتلة "defer" تُنفذ بسبب استثناء تم إطلاقه وأطلقت هي استثناءً آخر، فمن غير المحدد ما الذي سيحدث، سوى أن المستدعي سيتلقى استثناءً بالتأكيد.
إلى جانب إطلاق استثناء، لا يُسمح لكتلة "defer" بتغيير تدفق التحكم في الكود المحيط بها بأي شكل آخر. وبوجه خاص، لا يجوز لها التسبب في "return" للدالة المحتوية لها، ولا يجوز لها عمل "goto" لوسم، أو التحكم في حلقة محتوية باستخدام "next"، أو "last"، أو "redo". ومع ذلك، فإن هذه البنيات مسموح بها تمامًا داخل جسم "defer" نفسه.
use feature 'defer';
{
defer {
foreach ( 1 .. 5 ) {
last if $_ == 3; # هذا مسموح به
}
}
}
{
foreach ( 6 .. 10 ) {
defer {
last if $_ == 8; # هذا غير مسموح به
}
}
}
جمل Switch¶
لا يُنصح باستخدام ميزة "switch" في الشفرات الجديدة ويُحتفظ بها للتوافق مع الإصدارات السابقة.
بدءًا من Perl 5.10.1 (حسنًا، 5.10.0، لكنها لم تكن تعمل بشكل صحيح)، يمكنك قول
use feature "switch";
لتمكين ميزة المبدل. يعتمد هذا بشكل فضفاض على نسخة قديمة من اقتراح لراكو، لكنه لم يعد يشبه تركيب راكو. تحصل أيضًا على ميزة المبدل كلما صرحت بأن شفرتك تفضل العمل تحت إصدار من Perl بين 5.10 و 5.34. على سبيل المثال:
use v5.14;
تحت ميزة "switch"، يكتسب Perl الكلمات المفتاحية "given" و "when" و "default" و "continue" و "break". بدءًا من Perl 5.16، يمكن للمرء أن يسبق كلمات المبدل المفتاحية بـ "CORE::" للوصول إلى الميزة دون جملة "use feature". الكلمتان المفتاحيتان "given" و "when" تشبهان "switch" و "case" في لغات أخرى -- على الرغم من أن "continue" ليست كذلك -- لذا يمكن إعادة كتابة الشفرة في القسم السابق كـ
use v5.10.1;
for ($var) {
when (/^abc/) { $abc = 1 }
when (/^def/) { $def = 1 }
when (/^xyz/) { $xyz = 1 }
default { $nothing = 1 }
}
تُعد "foreach" طريقة أخرى لضبط الموضوعي. إذا كنت ترغب في استخدام "given"، فيمكن كتابة ذلك على النحو التالي:
use v5.10.1;
given ($var) {
when (/^abc/) { $abc = 1 }
when (/^def/) { $def = 1 }
when (/^xyz/) { $xyz = 1 }
default { $nothing = 1 }
}
اعتبارًا من 5.14، يمكن أيضًا كتابة ذلك بهذه الطريقة:
use v5.14;
for ($var) {
$abc = 1 when /^abc/;
$def = 1 when /^def/;
$xyz = 1 when /^xyz/;
default { $nothing = 1 }
}
أو إذا كنت لا تهتم بالبقاء في الجانب الآمن، هكذا:
use v5.14;
given ($var) {
$abc = 1 when /^abc/;
$def = 1 when /^def/;
$xyz = 1 when /^xyz/;
default { $nothing = 1 }
}
تكون معاملات "given" و "when" في سياق قياسي، وتقوم "given" بإسناد قيمة الموضوع لمتغير $_.
من الصعب وصف ما يفعله معامل EXPR في "when" بدقة، ولكن بشكل عام، فإنه يحاول تخمين ما تريد فعله. أحيانًا يتم تفسيره على أنه "$_ ~~ EXPR"، وأحيانًا لا. كما أنه يتصرف بشكل مختلف عندما يكون محاطًا معجميًا بكتلة "given" عما يفعله عندما يكون محاطًا ديناميكيًا بحلقة "foreach". القواعد أصعب بكثير من أن تُفهم وتوصف هنا. انظر "Experimental Details on given and when" لاحقًا.
بسبب خطأ مؤسف في كيفية تنفيذ "given" بين Perl 5.10 و 5.16، فإن نسخة $_ المحكومة بواسطة "given" في تلك التنفيذات هي مجرد نسخة ذات نطاق معجمي من الأصل، وليست اسمًا مستعارًا ذو نطاق ديناميكي للأصل، كما كان ينبغي أن تكون لو كانت "foreach" أو كما هو في مواصفات لغة Raku الأصلية والحالية. تم إصلاح هذا الخطأ في Perl 5.18 (وأُزيلت $_ المعجمية نفسها في Perl 5.24).
إذا كان الكود الخاص بك لا يزال يحتاج للعمل على إصدارات أقدم، فالتزم بـ "foreach" كموضوعي لك وستكون أقل تعاسة.
الانتقال (Goto)¶
على الرغم من أنها ليست لضعاف القلوب، إلا أن Perl يدعم جملة "goto". هناك ثلاثة أشكال: "goto"-LABEL و "goto"-EXPR و "goto"-&NAME. إن LABEL الحلقة ليس في الواقع هدفًا صالحًا لـ "goto"؛ إنه مجرد اسم للحلقة.
يجد نموذج "goto"-LABEL الجملة الموسومة بـ LABEL ويستأنف التنفيذ هناك. لا يجوز استخدامه للدخول في أي تركيب يتطلب تهيئة، مثل الروتين الفرعي أو حلقة "foreach". كما لا يمكن استخدامه للدخول في تركيب تم تحسينه بعيدًا. يمكن استخدامه للذهاب إلى أي مكان آخر تقريبًا داخل النطاق الديناميكي، بما في ذلك خارج الروتينات الفرعية، ولكن عادةً ما يكون من الأفضل استخدام تركيب آخر مثل "last" أو "die". لم يشعر مؤلف Perl أبدًا بالحاجة إلى استخدام هذا النموذج من "goto" (في Perl، أي -- أما في C فالأمر مختلف).
يتوقع نموذج "goto"-EXPR اسم وسم، والذي سيتم حل نطاقه ديناميكيًا. يتيح ذلك جمل "goto" المحسوبة كما في FORTRAN، ولكن لا يُنصح به بالضرورة إذا كنت تعمل على تحسين قابلية الصيانة:
goto(("FOO", "BAR", "GLARCH")[$i]);
يعد شكل &NAME-"goto" سحرياً للغاية، ويستبدل استدعاء الروتين الفرعي المسمى بالروتين الفرعي الذي يعمل حالياً. يُستخدم هذا بواسطة روتينات AUTOLOAD() التي ترغب في تحميل روتين فرعي آخر ثم التظاهر بأن الروتين الآخر قد استُدعي في المقام الأول (باستثناء أن أي تعديلات على @_ في الروتين الفرعي الحالي تُمرر إلى الروتين الفرعي الآخر). بعد "goto"، لن يتمكن حتى caller() من معرفة أن هذا الروتين قد استُدعي أولاً.
في جميع الحالات مثل هذه تقريبًا، عادةً ما تكون فكرة أفضل بكثير استخدام آليات تدفق التحكم المهيكلة مثل "next" أو "last" أو "redo" بدلاً من اللجوء إلى "goto". بالنسبة لبعض التطبيقات، يمكن أن يكون زوج الالتقاط والرمي "eval{}" و die() لمعالجة الاستثناءات نهجًا حكيمًا أيضًا.
عبارة الحذف (The Ellipsis Statement)¶
بدءًا من Perl 5.12، يقبل Perl علامة الحذف، ""...""، كعنصر نائب للشفرة التي لم تنفذها بعد. عندما يواجه Perl 5.12 أو أحدث جملة تحتوي على علامة حذف، فإنه يحللها دون خطأ، ولكن إذا حاولت تنفيذها فعليًا، فإن Perl يرمي استثناءً بالنص "Unimplemented":
use v5.12;
sub unimplemented { ... }
eval { unimplemented() };
if ($@ =~ /^Unimplemented at /) {
say "I found an ellipsis!";
}
يمكنك فقط استخدام عبارة الحذف كبديل لعبارة كاملة. من الناحية النحوية، ""...;"" هي عبارة كاملة، ولكن، كما هو الحال مع أنواع أخرى من العبارات المنتهية بفاصلة منقوطة، يجوز حذف الفاصلة المنقوطة إذا ظهرت ""..."" مباشرة قبل قوس الإغلاق. توضح هذه الأمثلة كيفية عمل علامة الحذف:
use v5.12;
{ ... }
sub foo { ... }
...;
eval { ... };
sub somemeth {
my $self = shift;
...;
}
$x = do {
my $n;
...;
say "Hurrah!";
$n;
};
لا يمكن لعبارة الحذف أن تحل محل تعبير يمثل جزءاً من عبارة أكبر. هذه الأمثلة لمحاولات استخدام علامة الحذف هي أخطاء نحوية:
use v5.12;
print ...;
open(my $fh, ">", "/dev/passwd") or ...;
if ($condition && ... ) { say "Howdy" };
... if $x > $y;
say "Cromulent" if ...;
$flub = 5 + ...;
هناك بعض الحالات التي لا يستطيع فيها Perl التمييز على الفور بين التعبير والجملة. على سبيل المثال، يبدو بناء جملة الكتلة ومنشئ مرجع الخبيئة المجهول متشابهين ما لم يكن هناك شيء في الأقواس المتعرجة لإعطاء تلميح لـ Perl. علامة الحذف هي خطأ نحوي إذا لم يخمن Perl أن "{ ... }" هي كتلة. داخل كتلتك، يمكنك استخدام ";" قبل علامة الحذف للإشارة إلى أن "{ ... }" هي كتلة وليست منشئ مرجع خبيئة.
ملاحظة: يشير بعض الأشخاص بالعامية إلى علامة الترقيم هذه باسم "yada-yada" أو "النقاط الثلاث"، ولكن اسمها الحقيقي هو في الواقع علامة حذف (ellipsis).
PODs: التوثيق المضمن¶
يحتوي Perl على آلية لخلط الوثائق مع الشفرة المصدرية. وبينما يتوقع بداية جملة جديدة، إذا واجه المصرف سطرًا يبدأ بعلامة يساوي وكلمة، مثل هذا
=head1 Here There Be Pods!
سيُتجاهل هذا النص وكل النص المتبقي حتى السطر الذي يبدأ بـ "=cut" وما يتضمنه. يوصَف تنسيق النص المتداخل في perlpod.
يسمح لك هذا بخلط الكود المصدري ونصوص التوثيق بحرية، كما في
=item snazzle($)
The snazzle() function will behave in the most spectacular
form that you can possibly imagine, not even excepting
cybernetic pyrotechnics.
=cut back to the compiler, nuff of this pod stuff!
sub snazzle($) {
my $thingie = shift;
.........
}
لاحظ أن مترجمي pod يجب أن ينظروا فقط إلى الفقرات التي تبدأ بتوجيه pod (هذا يسهل التحليل)، بينما يعرف المترجم في الواقع كيفية البحث عن مهربات pod حتى في منتصف الفقرة. وهذا يعني أن الأشياء السرية التالية سيتم تجاهلها من قبل كل من المترجم والمترجمين.
$x=3;
=secret stuff
warn "Neither POD nor CODE!?"
=cut back
print "got $x\n";
ربما لا ينبغي عليك الاعتماد على كون warn() خارج النطاق (podded out) للأبد. ليست كل مترجمات pod حسنة السلوك في هذا الصدد، وربما يصبح المصرف أكثر تطلبًا.
يمكن للمرء أيضاً استخدام توجيهات pod لتحويل قسم من الكود إلى تعليق بسرعة.
التعليقات العادية القديمة (ليست كذلك!)¶
يمكن لـ Perl معالجة توجيهات الأسطر، تمامًا مثل معالج C المسبق. وباستخدام هذا، يمكن للمرء التحكم في فكرة Perl عن أسماء الملفات وأرقام الأسطر في رسائل الخطأ أو التحذير (خاصة للسلاسل النصية التي تُعالج بـ eval()). بناء الجملة لهذه الآلية هو نفسه تقريبًا لمعظم معالجات C المسبقة: فهو يطابق التعبير النمطي
# example: '# line 42 "new_filename.plx"'
/^\# \s*
line \s+ (\d+) \s*
(?:\s("?)([^"]+)\g2)? \s*
$/x
حيث يكون $1 هو رقم السطر للسطر التالي، و $3 هو اسم الملف الاختياري (محدد مع أو بدون علامات اقتباس). لاحظ أنه لا يجوز أن تسبق أي مسافة بيضاء علامة "#"، على عكس معالجات C المسبقة الحديثة.
هناك فخ واضح تمامًا متضمن في توجيه السطر: ستظهر المنقحات والمحللات فقط آخر سطر مصدري يظهر عند رقم سطر معين في ملف معين. يجب توخي الحذر لتجنب تضارب أرقام الأسطر في الشفرة التي ترغب في تنقيحها لاحقًا.
فيما يلي بعض الأمثلة التي يجب أن تكون قادراً على كتابتها في صدفة الأوامر الخاصة بك:
% perl
# line 200 "bzzzt"
# the '#' on the previous line must be the first char on line
die 'foo';
__END__
foo at bzzzt line 201.
% perl
# line 200 "bzzzt"
eval qq[\n#line 2001 ""\ndie 'foo']; print $@;
__END__
foo at - line 2001.
% perl
eval qq[\n#line 200 "foo bar"\ndie 'foo']; print $@;
__END__
foo at foo bar line 200.
% perl
# line 345 "goop"
eval "\n#line " . __LINE__ . ' "' . __FILE__ ."\"\ndie 'foo'";
print $@;
__END__
foo at goop line 345.
تفاصيل تجريبية حول given و when¶
كما ذُكر سابقاً، تُعتبر ميزة "المبدل" (switch) تجريبية للغاية (ومن المقرر أيضاً إزالتها في perl 5.42.0)؛ وهي عرضة للتغيير بإشعار ضئيل. وبشكل رئيس، تتميز "when" بسلوكيات خادعة يُتوقع أن تتغير لتصبح أقل خداعاً في المستقبل. لا تعتمد على تنفيذها (الخاطئ) الحالي. قبل Perl 5.18، كان لـ "given" أيضاً سلوكيات خادعة يجب أن تظل حذراً منها إذا كان الكود الخاص بك يجب أن يعمل على إصدارات أقدم من Perl.
فيما يلي مثال أطول لـ "given":
use feature ":5.10";
given ($foo) {
when (undef) {
say '$foo is undefined';
}
when ("foo") {
say '$foo is the string "foo"';
}
when ([1,3,5,7,9]) {
say '$foo is an odd digit';
continue; # Fall through
}
when ($_ < 100) {
say '$foo is numerically less than 100';
}
when (\&complicated_check) {
say 'a complicated check for $foo is true';
}
default {
die q(I don't know what to do with $foo);
}
}
قبل Perl 5.18، كانت given(EXPR) تخصص قيمة EXPR لمجرد نسخة (!) ذات نطاق معجمي من $_، وليس اسماً مستعاراً ذا نطاق ديناميكي بالطريقة التي تعمل بها "foreach". وهذا جعلها مشابهة لـ
do { my $_ = EXPR; ... }
إلا أنه كان يُخرج من الكتلة آلياً بواسطة "when" ناجحة أو "break" صريحة. ونظراً لأنها كانت مجرد نسخة، ولأنها كانت ذات نطاق معجمي فقط، وليست ذات نطاق ديناميكي، لم يكن بإمكانك القيام بأشياء بها مما اعتدت عليه في حلقة "foreach". وبشكل خاص، لم تكن تعمل مع استدعاءات الوظائف التعسفية إذا كانت تلك الوظائف قد تحاول الوصول إلى $_. من الأفضل الالتزام بـ "foreach" في تلك الحالة.
تأتي معظم القوة من المطابقة الذكية الضمنية التي يمكن تطبيقها أحياناً. في معظم الأوقات، تُعامل when(EXPR) كمطابقة ذكية ضمنية لـ $_، أي "$_ ~~ EXPR". (انظر "Smartmatch Operator" في perlop لمزيد من المعلومات حول المطابقة الذكية.) ولكن عندما يكون EXPR واحداً من الحالات الاستثنائية العشر (أو أشياء مثلها) المدرجة أدناه، فإنه يُستخدم مباشرة كقيمة منطقية.
- 1.
- استدعاء روتين فرعي معرف من قبل المستخدم أو استدعاء طريقة.
- 2.
- مطابقة تعبير نمطي في شكل "/REGEX/"، أو "$foo =~ /REGEX/"، أو "$foo =~ EXPR". أيضاً، مطابقة تعبير نمطي منفي في شكل "!/REGEX/"، أو "$foo !~ /REGEX/"، أو "$foo !~ EXPR".
- 3.
- مطابقة
ذكية
تستخدم
عامل "~~"
صريح، مثل
"EXPR ~~ EXPR".
ملاحظة: سيتعين عليك غالباً استخدام "$c ~~ $_" لأن الحالة المبدئية تستخدم "$_ ~~ $c"، وهو ما يكون غالباً عكس ما تريده.
- 4.
- عامل مقارنة منطقي مثل "$_ < 10" أو "$x eq "abc"". العوامل العلائقية التي ينطبق هذا عليها هي المقارنات الرقمية الست ("<"، و ">"، و "<="، و ">="، و "=="، و "!=")، ومقارنات السلاسل الست ("lt"، و "gt"، و "le"، و "ge"، و "eq"، و "ne").
- 5.
- على الأقل الوظائف الثلاث المضمنة defined(...) و exists(...) و eof(...). قد نضيف المزيد منها لاحقاً يوماً ما إذا فكرنا فيها.
- 6.
- تعبير منفي، سواء كان "!(EXPR)" أو not(EXPR)، أو أو منطقي حصري (xor)، "(EXPR1) xor (EXPR2)". إصدارات البتات (bitwise) (مثل "~" و "^") غير مدرجة.
- 7.
- عامل اختبار ملف، مع 4 استثناءات بالضبط: "-s" و "-M" و "-A" و "-C"، لأن هذه تعيد قيماً رقمية وليست منطقية. عامل اختبار الملف "-z" غير مدرج في قائمة الاستثناءات.
- 8.
- عاملا الوثّاب (flip-flop) وهما ".." و "...". لاحظ أن عامل الوثّاب "..." مختلف تماماً عن عبارة الحذف "..." التي وُصفت للتو.
في الحالات الثماني المذكورة أعلاه، تُستخدم قيمة EXPR مباشرة كقيمة منطقية، لذا لا تُجرى مطابقة ذكية. يمكنك التفكير في "when" على أنها مطابقة ذكية ذكية (smartsmartmatch).
علاوة على ذلك، تفحص Perl معاملات العوامل المنطقية لتقرر ما إذا كانت ستستخدم المطابقة الذكية لكل منها من خلال تطبيق الاختبار أعلاه على المعاملات:
- 9.
- إذا كان EXPR هو "EXPR1 && EXPR2" أو "EXPR1 and EXPR2"، فسيُطبق الاختبار بشكل عودي على كل من EXPR1 و EXPR2. فقط إذا نجح كلا المعاملين في الاختبار أيضاً، بشكل عودي، فسوف يُعامل التعبير كقيمة منطقية. وإلا، تُستخدم المطابقة الذكية.
- 10.
- إذا كان EXPR هو "EXPR1 || EXPR2"، أو "EXPR1 // EXPR2"، أو "EXPR1 or EXPR2"، فسيُطبق الاختبار بشكل عودي على EXPR1 فقط (والذي قد يكون هو نفسه عامل AND ذو أسبقية أعلى، على سبيل المثال، وبالتالي يخضع للقاعدة السابقة)، وليس على EXPR2. إذا كان EXPR1 سيستخدم المطابقة الذكية، فإن EXPR2 يفعل ذلك أيضاً، بغض النظر عما يحتويه EXPR2. ولكن إذا لم يتمكن EXPR2 من استخدام المطابقة الذكية، فلن يكون المعامل الثاني كذلك أيضاً. هذا مختلف تماماً عن حالة "&&" الموصوفة للتو، لذا كن حذراً.
هذه القواعد معقدة، لكن الهدف منها هو أن تفعل ما تريد (حتى لو لم تفهم تمامًا سبب قيامها بذلك). على سبيل المثال:
when (/^\d+$/ && $_ < 75) { ... }
سيُعامل كمطابقة منطقية لأن القواعد تقول إن كلاً من مطابقة التعبير النمطي والاختبار الصريح على $_ سيُعاملان كقيم منطقية.
أيضاً:
when ([qw(foo bar)] && /baz/) { ... }
سيستخدم المطابقة الذكية لأن واحداً فقط من المعاملات هو قيمة منطقية: الآخر يستخدم المطابقة الذكية، وهذا هو ما يفوز.
علاوة على ذلك:
when ([qw(foo bar)] || /^baz/) { ... }
سيستخدم المطابقة الذكية (يُؤخذ المعامل الأول فقط في الاعتبار)، بينما
when (/^baz/ || [qw(foo bar)]) { ... }
سيختبر التعبير النمطي فقط، مما يؤدي إلى معاملة كلا المعاملين كقيم منطقية. احذر من هذا الأمر إذاً، لأن مرجع المصفوفة هو دائماً قيمة صحيحة (true)، مما يجعله زائداً عن الحاجة فعلياً. ليست فكرة جيدة.
لا تزال العمليات المنطقية الحشوية سيتم تحسينها بعيدًا. لا تنجرف لكتابة
when ("foo" or "bar") { ... }
سيُحسن هذا ليقتصر على "foo"، لذا لن يُنظر في "bar" أبداً (على الرغم من أن القواعد تقول باستخدام مطابقة ذكية على "foo"). بالنسبة لتبديل مثل هذا، سيعمل مرجع المصفوفة، لأن هذا سيحرض على المطابقة الذكية:
when ([qw(foo bar)] { ... }
هذا يعادل إلى حد ما وظيفة السقوط (fallthrough) في جملة switch بأسلوب C (لا ينبغي الخلط بينها وبين وظيفة السقوط في Perl -- انظر أدناه)، حيث تُستخدم نفس الكتلة لعدة جمل "case".
اختصار مفيد آخر هو أنه إذا استخدمت مصفوفة حرفية أو هاش كمعامل لـ "given"، فإنه يُحول إلى مرجع. لذا فإن given(@foo) هي نفسها given(\@foo)، على سبيل المثال.
تتصرف "default" تماماً مثل "when(1 == 1)"، أي أنها تطابق دائماً.
الخروج (Breaking out)
يمكنك استخدام الكلمة المفتاحية "break" للخروج من كتلة "given" المحيطة. كل كتلة "when" تنتهي ضمنياً بـ "break".
السقوط (Fall-through)
يمكنك استخدام الكلمة المفتاحية "continue" للسقوط من حالة إلى حالة "when" أو "default" التالية مباشرة:
given($foo) {
when (/x/) { say '$foo contains an x'; continue }
when (/y/) { say '$foo contains a y' }
default { say '$foo does not contain a y' }
}
قيمة الإرجاع
عندما تكون جملة "given" تعبيرًا صالحًا أيضًا (على سبيل المثال، عندما تكون الجملة الأخيرة في كتلة)، فإنها تؤول إلى:
- قائمة فارغة بمجرد مواجهة "break" صريحة.
- قيمة آخر تعبير جرى تقييمه في بند "when"/"default" الناجح، إذا وجد واحد.
- قيمة آخر تعبير وُجدت قيمته في كتلة "given" إذا لم يتحقق أي شرط.
في كلتا الحالتين الأخيرتين، تُحسب قيمة التعبير الأخير في السياق الذي طُبق على كتلة "given".
لاحظ أنه، على عكس "if" و "unless"، فإن جمل "when" الفاشلة تُنتج دائمًا قائمة فارغة.
my $price = do {
given ($item) {
when (["pear", "apple"]) { 1 }
break when "vote"; # لا يمكن شراء صوتي
1e10 when /Mona Lisa/;
"unknown";
}
};
حاليًا، لا يمكن دائمًا استخدام كتل "given" كتعبيرات حقيقية. قد يُعالج هذا في إصدار مستقبلي من Perl.
التبديل داخل حلقة تكرار
بدلاً من استخدام given()، يمكنك استخدام حلقة foreach(). على سبيل المثال، إليك طريقة واحدة لعد عدد مرات ظهور سلسلة نصية معينة في مصفوفة:
use v5.10.1;
my $count = 0;
for (@array) {
when ("foo") { ++$count }
}
print "\@array contains $count copies of 'foo'\n";
أو في إصدار أحدث:
use v5.14;
my $count = 0;
for (@array) {
++$count when "foo";
}
print "\@array contains $count copies of 'foo'\n";
في نهاية جميع كتل "when"، يوجد "next" ضمني. يمكنك تجاوز ذلك بـ "last" صريح إذا كنت مهتمًا بالمطابقة الأولى وحدها.
لا يعمل هذا إذا قمت بتحديد متغير حلقة بشكل صريح، كما في "for $item (@array)". يجب عليك استخدام المتغير المبدئي $_.
الاختلافات عن راكو
إن تراكيب المطابقة الذكية و "given"/"when" في Perl 5 ليست متوافقة مع نظيراتها في راكو. الفرق الأكثر وضوحًا والأقل أهمية هو أنه في Perl 5، الأقواس مطلوبة حول وسيط given() و when() (إلا عندما يُستخدم الأخير كمعدل جملة). الأقواس في راكو اختيارية دائمًا في تراكيب التحكم مثل if()، أو while()، أو when()؛ ولا يمكن جعلها اختيارية في Perl 5 دون قدر كبير من الارتباك المحتمل، لأن Perl 5 سيحلل التعبير
given $foo {
...
}
كما لو كان وسيط "given" عنصرًا في الخبيئة %foo، مفسرًا الأقواس المتعرجة على أنها بناء جملة لعنصر خبيئة.
ومع ذلك، هناك العديد والعديد من الاختلافات الأخرى. على سبيل المثال، هذا يعمل في Perl 5:
use v5.12;
my @primary = ("red", "blue", "green");
if (@primary ~~ "red") {
say "primary smartmatches red";
}
if ("red" ~~ @primary) {
say "red smartmatches primary";
}
say "that's all, folks!";
لكنه لا يعمل على الإطلاق في راكو. بدلاً من ذلك، يجب عليك استخدام عامل "any" (القابل للتوازي):
if any(@primary) eq "red" {
say "primary smartmatches red";
}
if "red" eq any(@primary) {
say "red smartmatches primary";
}
جدول المطابقات الذكية في "Smartmatch Operator" في perlop ليس متطابقًا مع ذلك المقترح في مواصفات راكو، ويرجع ذلك أساسًا إلى الاختلافات بين نماذج بيانات راكو و Perl 5، وأيضًا لأن مواصفات راكو تغيرت منذ أن سارعت Perl 5 إلى اعتماده مبكرًا.
في راكو، سيقوم when() دائمًا بمطابقة ذكية ضمنية مع وسيطه، بينما في Perl 5 يكون من المناسب (وإن كان مربكًا محتملًا) كبح هذه المطابقة الذكية الضمنية في حالات متنوعة وغير محددة بدقة، كما هو موضح أعلاه تقريبًا. (الفرق يرجع بشكل كبير إلى أن Perl 5 لا يملك، حتى داخليًا، نوعًا منطقيًا.)
ترجمة¶
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>
هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.
إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.
| 11 يوليو 2025 | perl v5.42.0 |