Scroll to navigation

PERLSYN(1) دليل مرجع مبرمجي بيرل PERLSYN(1)

الاسم

perlsyn - صياغة بيرل: التصريحات، والعبارات، والملاحظات

الوصف

يتكون برنامج بيرل من تسلسل من التصريحات والعبارات التي تعمل من الأعلى إلى الأسفل. تسمح لك الحلقات، والبرامج الفرعية، وهياكل التحكم الأخرى بالتنقل داخل الكود.

بيرل لغة حرة التنسيق: يمكنك تنسيقها وإزاحتها كما تشاء. تخدم المسافات البيضاء غالبا في فصل الوحدات (tokens)، على عكس لغات مثل بايثون حيث تعد جزءا رئيسا من الصياغة، أو فورتران حيث لا تهم فيها المسافات.

العديد من عناصر صياغة بيرل اختيارية. بدلا من إلزامك بوضع أقواس حول كل استدعاء دالة والتصريح عن كل متغير، يمكنك غالبا ترك هذه العناصر الصريحة وسوف تستنتج بيرل ما تقصده. يُعرف هذا باسم افعل ما أعنيه، ويُختصر بـ DWIM. إنه يسمح للمبرمجين بأن يكونوا كسالى وأن يكودوا بالأسلوب الذي يريحهم.

تستعير بيرل الصياغة والمفاهيم من لغات عديدة: awk، و sed، و C، وصدفة Bourne، و Smalltalk، و Lisp وحتى الإنجليزية. وقد استعارت لغات أخرى الصياغة من بيرل، وخاصة توسعات التعبيرات النمطية. لذا إذا كنت قد برمجت بلغة أخرى فستجد أجزاء مألوفة في بيرل. غالبا ما تعمل بنفس الطريقة، ولكن راجع perltrap للحصول على معلومات حول كيفية اختلافها.

التصريحات

الأشياء الوحيدة التي تحتاج إلى التصريح عنها في بيرل هي تنسيقات التقارير والبرامج الفرعية (وأحيانا ولا حتى البرامج الفرعية). يحمل المتغير القياسي القيمة غير المعرفة ("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"، أو أي قيمة تم إسنادها سابقا، أو ربما أي شيء آخر. لا تعتمد عليه. قد تفعل الإصدارات المستقبلية من بيرل شيئا مختلفا عن إصدار بيرل الذي تجربه عليه.

معدل "when" هو ميزة تجريبية ظهرت لأول مرة في بيرل 5.14. لاستخدامه، يجب عليك تضمين تصريح "use v5.14". (من الناحية التقنية، يتطلب فقط ميزة "switch"، ولكن هذا الجانب منها لم يكن متاحا قبل 5.14). وهو يعمل فقط من داخل حلقة "foreach" أو كتلة "given"، حيث ينفذ العبارة فقط إذا كان المطابق الذكي "$_ ~~ EXPR" صحيحا. إذا نُفذت العبارة، فسيتبعها "next" من داخل "foreach" و "break" من داخل "given".

في ظل التنفيذ الحالي، يمكن أن تكون حلقة "foreach" في أي مكان داخل النطاق الديناميكي لمعدل "when"، ولكن يجب أن تكون داخل النطاق المعجمي لكتلة "given". قد يتم تخفيف هذا القيد في إصدار مستقبلي. راجع "عبارات التبديل" أدناه.

العبارات المركبة

في بيرل، يُطلق على تسلسل العبارات الذي يحدد نطاقا اسم كتلة. أحيانا يتم تحديد الكتلة بواسطة الملف الذي يحتوي عليها (في حالة ملف مطلوب، أو البرنامج ككل)، وأحيانا يتم تحديد الكتلة بمدى سلسلة نصية (في حالة 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. يؤدي هذا السلوك اليائس إلى إطلاق تحذير إذا كنت تستخدم pragma الخاص بـ "use warnings" أو علم -w.

إذا كان تعبير الشرط لعبارة "while" مبنيا على أي من مجموعة من أنواع التعبيرات التكرارية، فإنه يحصل على بعض المعاملة السحرية. أنواع التعبيرات التكرارية المتأثرة هي "readline"، وعامل الإدخال "<FILEHANDLE>"، و "readdir"، و "glob"، وعامل المطابقة "<PATTERN>"، و "each". إذا كان تعبير الشرط أحد أنواع التعبيرات هذه، فسيتم إسناد القيمة الناتجة عن العامل التكراري ضمنيا إلى $_. إذا كان تعبير الشرط أحد أنواع التعبيرات هذه أو إسنادا صريحا لأحدهما إلى قيمة قياسية، فإن الشرط يختبر في الواقع تعريف قيمة التعبير، وليس قيمته الصحيحة العادية.

إذا كان هناك "continue" BLOCK، فإنه يُنفذ دائما قبل تقييم الشرط مرة أخرى مباشرة. وبالتالي يمكن استخدامه لزيادة متغير الحلقة، حتى عندما تستمر الحلقة عبر عبارة "next".

عندما تُسبق الكتلة بكلمة مفتاحية لمرحلة التصريف مثل "BEGIN"، أو "END"، أو "INIT"، أو "CHECK"، أو "UNITCHECK"، فستعمل الكتلة فقط خلال مرحلة التنفيذ المقابلة. راجع perlmod لمزيد من التفاصيل.

يمكن لوحدات التوسعة أيضا ربط نفسها بمحلل بيرل لتحديد أنواع جديدة من العبارات المركبة. يتم تقديمها بواسطة كلمة مفتاحية تتعرف عليها التوسعة، وتُعرف الصياغة التي تلي الكلمة المفتاحية بالكامل بواسطة التوسعة. إذا كنت منفذا، فراجع "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 في بيرل مثل حلقة "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" أن تُستخدم في تطبيقات أخرى مثيرة للاهتمام. إليك أحدها الذي يتجنب المشكلة التي تحدث عند الاختبار الصريح لنهاية الملف (EOF) على واصف ملف تفاعلي مما يتسبب في ظهور البرنامج وكأنه معلق.

    $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" هو اسم مستعار (alias) ضمني لكل عنصر في القائمة التي تكرر عبرها.

إذا كان أي جزء من LIST عبارة عن مصفوفة، فإن "foreach" ستصبح مرتبكة للغاية إذا أضفت أو أزلت عناصر داخل جسم الحلقة، على سبيل المثال باستخدام "splice". لذا لا تفعل ذلك.

ربما لن تفعل "foreach" ما تتوقعه إذا كان VAR متغيرًا مربوطًا (tied) أو متغيرًا خاصًا آخر. لا تفعل ذلك أيضًا.

بدءًا من Perl 5.22، هناك متغير تجريبي لهذه الحلقة يقبل متغيرًا مسبوقًا بشرطة مائلة عكسية (backslash) لـ VAR، وفي هذه الحالة يجب أن تكون العناصر في LIST مراجع (references). سيصبح المتغير المسبوق بشرطة مائلة عكسية اسمًا مستعارًا (alias) لكل عنصر مرجعي في LIST، والذي يجب أن يكون من النوع الصحيح. لا يشترط أن يكون المتغير قياسيًا في هذه الحالة، ويمكن أن تتبع الشرطة المائلة العكسية بـ "my". لاستخدام هذه الصيغة، يجب تفعيل ميزة "refaliasing" عبر "use feature". (انظر 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 خوارزمية معينة في بيرل:

    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
    }

بينما إليك كيف قد يفعل ذلك مبرمج بيرل أكثر ارتياحًا لأسلوب اللغة:

    OUTER: for my $wid (@ary1) {
    INNER:   for my $jet (@ary2) {
                next OUTER if $wid > $jet;
                $wid += $jet;
             }
          }

انظر كم هذا أسهل؟ إنه أنظف، وأكثر أمانًا، وأسرع. إنه أنظف لأنه أقل تعقيدًا. وأكثر أمانًا لأنه إذا أُضيف كود بين الحلقتين الداخلية والخارجية لاحقًا، فلن يُنفذ الكود الجديد عن طريق الخطأ. إن "next" تكرر بوضوح الحلقة الأخرى بدلاً من مجرد إنهاء الحلقة الداخلية. وهو أسرع لأن بيرل تنفذ جملة "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

بدءًا من Perl 5.10.1 (في الواقع 5.10.0، لكنها لم تكن تعمل بشكل صحيح)، يمكنك قول

    use feature "switch";

لتفعيل ميزة switch تجريبية. تعتمد هذه الميزة بشكل فضفاض على نسخة قديمة من مقترح Raku، لكنها لم تعد تشبه بنية Raku. ستحصل أيضًا على ميزة switch كلما صرحت بأن الكود الخاص بك يفضل العمل تحت إصدار من بيرل بين 5.10 و 5.34. على سبيل المثال:

    use v5.14;

تحت ميزة "switch"، تكتسب بيرل الكلمات المفتاحية التجريبية "given"، و "when"، و "default"، و "continue"، و "break". بدءًا من Perl 5.16، يمكن للمرء أن يسبق كلمات switch المفتاحية بـ "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" هي الطريقة غير التجريبية لضبط الموضوعي (topicalizer). إذا كنت ترغب في استخدام "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". هناك ثلاثة أشكال: LABEL-"goto" و EXPR-"goto" و &NAME-"goto". إن وسم (LABEL) الحلقة ليس في الواقع هدفاً صالحاً لعبارة "goto"؛ بل هو مجرد اسم للحلقة.

يبحث شكل LABEL-"goto" عن العبارة الموسومة بـ LABEL ويستأنف التنفيذ هناك. لا يجوز استخدامه للدخول في أي بنية تتطلب تهيئة، مثل الروتين الفرعي أو حلقة "foreach". كما لا يمكن استخدامه للدخول في بنية أُزيلت عبر التحسين. يمكن استخدامه للذهاب إلى أي مكان آخر تقريباً ضمن النطاق الديناميكي، بما في ذلك الخروج من الروتينات الفرعية، ولكن من الأفضل عادةً استخدام بنية أخرى مثل "last" أو "die". لم يشعر مؤلف Perl أبداً بالحاجة إلى استخدام هذا الشكل من "goto" (في Perl، أما في لغة C فالأمر مختلف).

يتوقع شكل EXPR-"goto" اسم وسم، والذي سيُحل نطاقه ديناميكياً. يسمح هذا بعبارات "goto" محسوبة كما في فورتران (FORTRAN)، ولكن لا يُنصح به بالضرورة إذا كنت تعمل على تحسين قابلية الصيانة:

    goto(("FOO", "BAR", "GLARCH")[$i]);

يعد شكل &NAME-"goto" سحرياً للغاية، ويستبدل استدعاء الروتين الفرعي المسمى بالروتين الفرعي الذي يعمل حالياً. يُستخدم هذا بواسطة روتينات AUTOLOAD() التي ترغب في تحميل روتين فرعي آخر ثم التظاهر بأن الروتين الآخر قد استُدعي في المقام الأول (باستثناء أن أي تعديلات على @_ في الروتين الفرعي الحالي تُمرر إلى الروتين الفرعي الآخر). بعد "goto"، لن يتمكن حتى caller() من معرفة أن هذا الروتين قد استُدعي أولاً.

في جميع الحالات تقريباً مثل هذه، عادةً ما تكون فكرة أفضل بكثير استخدام آليات تدفق التحكم الهيكلية مثل "next" أو "last" أو "redo" بدلاً من اللجوء إلى "goto". بالنسبة لتطبيقات معينة، يمكن أن يكون زوج (catch and throw) المتمثل في "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() مخفياً كـ pod للأبد. ليست كل مترجمات 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)، مما يجعله زائداً عن الحاجة فعلياً. ليست فكرة جيدة.

ستظل العوامل المنطقية الحشوية (Tautologous) تُزال عبر التحسين. لا تنجذب لكتابة

    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.

16 نوفمبر 2025 perl v5.40.1