Scroll to navigation

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

الاسم

perlref - مراجع بيرل وهياكل البيانات المتداخلة

ملاحظة

هذا توثيق كامل حول كافة جوانب المراجع. للحصول على مقدمة تعليمية أقصر للميزات الأساسية فقط، انظر perlreftut.

الوصف

قبل الإصدار 5 من بيرل، كان من الصعب تمثيل هياكل البيانات المعقدة، لأن جميع المراجع كان يجب أن تكون رمزية - وحتى حينها كان من الصعب الإشارة إلى متغير بدلاً من إدخال في جدول الرموز. الآن لا تكتفي بيرل بجعل استخدام المراجع الرمزية للمتغيرات أسهل فحسب، بل تتيح لك أيضًا امتلاك مراجع "صلبة" (hard) لأي قطعة من البيانات أو الكود. يمكن لأي كمية سلمية (scalar) أن تحمل مرجعًا صلبًا. وبما أن المصفوفات والمصفوفات المجزأة (hashes) تحتوي على سلميات، يمكنك الآن بسهولة بناء مصفوفات من مصفوفات، ومصفوفات من مجزئات، ومجزئات من مصفوفات، ومصفوفات من مجزئات دوال، وما إلى ذلك.

المراجع الصلبة ذكية - فهي تتبع أعداد المراجع لك، وتُحرر الشيء المشار إليه آليًا عندما يصل عدد مراجعة إلى الصفر. (قد لا تصل أعداد المراجع للقيم في هياكل البيانات ذاتية المرجعية أو الدائرية إلى الصفر دون القليل من المساعدة؛ انظر "Circular References" لشرح مفصل.) إذا صادف أن كان ذلك الشيء كائنًا، فسيُدمر الكائن. انظر perlobj لمزيد عن الكائنات. (بمعنى ما، كل شيء في بيرل هو كائن، لكننا عادة ما نحتفظ بالكلمة للمراجع إلى الكائنات التي تم "مباركتها" رسميًا في حزمة فئة.)

المراجع الرمزية هي أسماء لمتغيرات أو كائنات أخرى، تمامًا كما يحتوي الارتباط الرمزي في نظام ملفات Unix على مجرد اسم ملف. تدوين *glob هو نوع من المراجع الرمزية. (تُسمى المراجع الرمزية أحيانًا "مراجع لينة"، ولكن يُرجى عدم تسميتها كذلك؛ فالمراجع مربكة بما يكفي دون مرادفات عديمة الفائدة).

في المقابل، المراجع الصلبة تشبه الروابط الصلبة في نظام ملفات يونكس: فهي تُستخدم للوصول إلى كائن أساسي دون الاهتمام باسمه (الآخر). عندما تُستخدم كلمة "مرجع" دون صفة، كما في الفقرة التالية، فإنها عادة ما تتحدث عن مرجع صلب.

المراجع سهلة الاستخدام في Perl. هناك مبدأ رئيس واحد فقط: بشكل عام، لا يقوم Perl بأي مراجعة أو فك مراجع ضمني. عندما يحمل متغير مفرد مرجعًا، فإنه يتصرف دائمًا كمتغير مفرد بسيط. ولا يبدأ سحريًا في أن يكون مصفوفة أو مجموعة مترابطة أو دالة فرعية؛ عليك إخباره صراحةً بالقيام بذلك، عن طريق فك مراجعة المتغير.

إنشاء المراجع

يمكن إنشاء المراجع بعدة طرق.

عامل الخط المائل العكسي

باستخدام معامل الخط المائل العكسي على متغير أو دالة فرعية أو قيمة. (هذا يعمل بشكل مشابه جدًا لمعامل & (عنوان الـ) في لغة C.) يؤدي هذا عادةً إلى إنشاء مرجع آخر لمتغير، لأن هناك بالفعل مرجعًا للمتغير في جدول الرموز. ولكن قد يختفي مرجع جدول الرموز، وسيظل لديك المرجع الذي أعاده الخط المائل العكسي. فيما يلي بعض الأمثلة:

    $scalarref = \$foo;
    $arrayref  = \@ARGV;
    $hashref   = \%ENV;
    $coderef   = \&handler;
    $globref   = \*foo;

ليس من الممكن إنشاء مرجع حقيقي لمقبض إدخال/إخراج (IO handle) (مقبض ملف أو مقبض دليل) باستخدام معامل الخط المائل العكسي. أقصى ما يمكنك الحصول عليه هو مرجع لـ typeglob، وهو في الواقع إدخال كامل في جدول الرموز. ولكن انظر شرح صيغة *foo{THING} أدناه. ومع ذلك، لا يزال بإمكانك استخدام type globs و globrefs كما لو كانت مقابض إدخال/إخراج.

الأقواس المربعة

يمكن إنشاء مرجع لمصفوفة مجهولة باستخدام الأقواس المربعة:

    $arrayref = [1, 2, ['a', 'b', 'c']];

لقد أنشأنا هنا مرجعًا لمصفوفة مجهولة مكونة من ثلاثة عناصر، وعنصرها الأخير هو نفسه مرجع لمصفوفة مجهولة أخرى مكونة من ثلاثة عناصر. (يمكن استخدام الصيغة متعددة الأبعاد الموصوفة لاحقًا للوصول إلى ذلك. على سبيل المثال، بعد ما سبق، ستكون قيمة "$arrayref->[2][1]" هي "b".)

أخذ مرجع لقائمة معدودة ليس مثل استخدام الأقواس المربعة -- بدلاً من ذلك، هو نفس إنشاء قائمة من المراجع!

    @list = (\$x, \@y, \%z);
    @list = \($x, @y, %z);      # نفس الشيء!

كحالة خاصة، يعيد "\(@foo)" قائمة مراجع لمحتويات @foo، وليس مرجعًا لـ @foo نفسها. وبالمثل بالنسبة لـ %foo، باستثناء أن مراجع المفاتيح تكون لنسخ (بما أن المفاتيح هي مجرد سلاسل نصية وليست سلميات كاملة).

الأقواس المعقوفة

يمكن إنشاء مرجع لمصفوفة مجزأة مجهولة باستخدام الأقواس المعقوفة:

    $hashref = {
        'Adam'  => 'Eve',
        'Clyde' => 'Bonnie',
    };

يمكن خلط مؤلفي المجزئات والمصفوفات المجهولة مثل هذه بحرية لإنتاج هيكل معقد كما تريد. بناء الجملة متعدد الأبعاد الموصوف أدناه يعمل لهذه أيضًا. القيم أعلاه هي قيم حرفية، لكن المتغيرات والتعبيرات ستعمل بنفس الكفاءة، لأن عوامل التعيين في بيرل (حتى داخل local() أو my()) هي عبارات قابلة للتنفيذ، وليست تصريحات وقت التصريف.

نظرًا لأن الأقواس المتعرجة (braces) تُستخدم لأشياء أخرى متعددة بما في ذلك الكتل (BLOCKs)، فقد تضطر أحيانًا إلى إزالة اللبس عن الأقواس في بداية الجملة بوضع "+" أو "return" في المقدمة حتى يدرك Perl أن القوس الافتتاحي لا يبدأ كتلة (BLOCK). وتُعتبر القيمة الاقتصادية والتذكارية لاستخدام الأقواس المتعرجة تستحق هذا العناء الإضافي العرضي.

على سبيل المثال، إذا كنت تريد دالة لإنشاء مجزأة جديدة وإعادة مرجع إليها، فلديك هذه الخيارات:

    sub hashem {        { @_ } }   # خاطئة بصمت
    sub hashem {       +{ @_ } }   # جيدة
    sub hashem { return { @_ } }   # جيدة

من ناحية أخرى، إذا كنت تريد المعنى الآخر، يمكنك فعل ذلك:

    sub showem {        { @_ } }   # غامضة (جيدة حاليًا،
                                   # ولكن قد تتغير)
    sub showem {       {; @_ } }   # جيدة
    sub showem { { return @_ } }   # جيدة

تعمل العلامات البادئة "+{" و "{;" دائمًا على إزالة الغموض عن التعبير ليعني إما مرجع HASH أو الكتلة (BLOCK).

الدوال الفرعية المجهولة

يمكن إنشاء مرجع لدالة فرعية مجهولة باستخدام "sub" بدون اسم للدالة:

    $coderef = sub { print "Boink!\n" };

لاحظ الفاصلة المنقوطة. باستثناء أن الكود الموجود بالداخل لا يُنفذ على الفور، فإن "sub {}" لا يُعد تصريحًا بقدر ما هو معامل، مثل "do{}" أو "eval{}". (ومع ذلك، بغض النظر عن عدد مرات تنفيذ هذا السطر المحدد (ما لم تكن في eval("..."))، سيظل لدى $coderef مرجع لنفس الدالة الفرعية المجهولة نفسها.)

تعمل الدوال الفرعية المجهولة كإغلاقات (closures) فيما يتعلق بمتغيرات my()، أي المتغيرات المرئية معجميًا ضمن النطاق الحالي. الإغلاق هو مفهوم من عالم Lisp يقول إنه إذا عرفت دالة مجهولة في سياق معجمي معين، فإنها تتظاهر بالعمل في ذلك السياق حتى عندما تُستدعى خارج السياق.

بالمصطلحات البشرية، إنها طريقة مضحكة لتمرير الوسائط إلى دالة فرعية عند تعريفها وكذلك عند استدعائها. وهي مفيدة لإعداد أجزاء صغيرة من الكود لتعمل لاحقًا، مثل ردود النداء (callbacks). يمكنك حتى القيام بأشياء موجهة للكائنات بها، على الرغم من أن Perl يوفر بالفعل آلية مختلفة للقيام بذلك -- انظر perlobj.

قد تفكر أيضًا في الإغلاق كطريقة لكتابة قالب دالة فرعية دون استخدام eval(). إليك مثال صغير لكيفية عمل الإغلاقات:

    sub newprint {
        my $x = shift;
        return sub { my $y = shift; print "$x, $y!\n"; };
    }
    $h = newprint("Howdy");
    $g = newprint("Greetings");
    # يمر الوقت...
    &$h("world");
    &$g("earthlings");

يطبع هذا

    Howdy, world!
    Greetings, earthlings!

لاحظ بشكل خاص أن $x يستمر في الإشارة إلى القيمة الممررة إلى newprint() على الرغم من خروج "my $x" من النطاق بحلول وقت تشغيل الدالة الفرعية المجهولة. هذا هو جوهر الإغلاق.

بالمناسبة، ينطبق هذا فقط على المتغيرات المعجمية. تستمر المتغيرات الديناميكية في العمل كما عملت دائمًا. الإغلاق ليس شيئًا يحتاج معظم مبرمجي بيرل لإزعاج أنفسهم به في البداية.

المنشئات

غالبًا ما تُعاد المراجع بواسطة دوال فرعية خاصة تسمى المنشئات (constructors). كائنات Perl هي مجرد مراجع لنوع خاص من الكائنات التي تعرف بالصدفة الحزمة المرتبطة بها. المنشئات هي مجرد دوال فرعية خاصة تعرف كيفية إنشاء هذا الارتباط. وهي تفعل ذلك من خلال البدء بمرجع عادي، ويظل مرجعًا عاديًا حتى عندما يكون كائنًا أيضًا. غالبًا ما تُسمى المنشئات new(). يمكنك استدعاؤها بشكل غير مباشر:

    $objref = new Doggie( Tail => 'short', Ears => 'long' );

ولكن يمكن أن ينتج عن ذلك صيغة غامضة في حالات معينة، لذا غالبًا ما يكون من الأفضل استخدام نهج استدعاء الطريقة المباشر:

    $objref   = Doggie->new(Tail => 'short', Ears => 'long');
    use Term::Cap;
    $terminal = Term::Cap->Tgetent( { OSPEED => 9600 });
    use Tk;
    $main    = MainWindow->new();
    $menubar = $main->Frame(-relief              => "raised",
                            -borderwidth         => 2)

يتوفر بناء جملة الكائن غير المباشر هذا فقط عندما تكون ميزة "use feature "indirect"" فعالة، وهذا ليس هو الحال عند طلب "use v5.36" (أو أعلى)، فمن الأفضل تجنب بناء جملة الكائن غير المباشر تمامًا.

النشوء الذاتي (Autovivification)

يمكن للمراجع من النوع المناسب أن تظهر للوجود إذا قمت بفك مراجعها في سياق يفترض وجودها. ولأننا لم نتحدث عن فك المراجع بعد، فلا يمكننا عرض أي أمثلة لك بعد.

فتحات Typeglob

يمكن إنشاء مرجع باستخدام بناء جملة خاص، يُعرف بمحبة باسم بناء جملة *foo{THING}. يعيد *foo{THING} مرجعًا لفتحة THING في *foo (وهي إدخال جدول الرموز الذي يحمل كل شيء يُعرف باسم foo).

    $scalarref = *foo{SCALAR};
    $arrayref  = *ARGV{ARRAY};
    $hashref   = *ENV{HASH};
    $coderef   = *handler{CODE};
    $ioref     = *STDIN{IO};
    $globref   = *foo{GLOB};
    $formatref = *foo{FORMAT};
    $globname  = *foo{NAME};    # "foo"
    $pkgname   = *foo{PACKAGE}; # "main"

معظم هذه الأشياء تشرح نفسها، لكن *foo{IO} يستحق اهتمامًا خاصًا. فهو يعيد مقبض الإدخال/الإخراج، المستخدم لمقابض الملفات ("open" في perlfunc)، والمقابس (sockets) ("socket" في perlfunc و "socketpair" في perlfunc)، ومقابض الأدلة ("opendir" في perlfunc). للتوافق مع الإصدارات السابقة من بيرل، يعد *foo{FILEHANDLE} مرادفًا لـ *foo{IO}، على الرغم من عدم تشجيعه، للتشجيع على الاستخدام المتسق لاسم واحد: IO. في إصدارات بيرل بين v5.8 و v5.22، سيصدر تحذير بشأن الإهمال، ولكن تم التراجع عن هذا الإهمال منذ ذلك الحين.

يعيد *foo{THING} القيمة undef إذا لم يُستخدم هذا الـ THING المعين بعد، باستثناء حالة المتغيرات المفردة (scalars). يعيد *foo{SCALAR} مرجعًا لمتغير مفرد مجهول إذا لم يُستخدم $foo بعد. قد يتغير هذا في إصدار مستقبلي.

يعد *foo{NAME} و *foo{PACKAGE} الاستثناء، حيث يعيدان سلاسل نصية بدلاً من المراجع. وتعيد هذه الحزمة والاسم الخاص بـ typeglob نفسه، بدلاً من تلك التي عُينت له. لذا، بعد "*foo=*Foo::bar"، سيصبح *foo هو "*Foo::bar" عند استخدامه كسلسلة نصية، لكن *foo{PACKAGE} و *foo{NAME} سيستمران في إنتاج "main" و "foo" على التوالي.

*foo{IO} هو بديل لآلية *HANDLE الواردة في "Typeglobs and Filehandles" في perldata لتمرير مقابض الملفات إلى الدوال الفرعية أو منها، أو تخزينها في هياكل بيانات أكبر. عيبه هو أنه لن ينشئ مقبض ملف جديد لك. وميزته هي أن هناك خطرًا أقل لإتلاف أكثر مما تريد باستخدام إسناد typeglob. (ومع ذلك، فإنه لا يزال يخلط بين مقابض الملفات والدلائل.) ولكن إذا أسندت القيمة الواردة إلى متغير مفرد بدلاً من typeglob كما نفعل في الأمثلة أدناه، فلا يوجد خطر لحدوث ذلك.

    splutter(*STDOUT);          # تمرير الـ glob بالكامل
    splutter(*STDOUT{IO});      # تمرير مقابض الملفات والأدلة معًا
    sub splutter {
        my $fh = shift;
        print $fh "her um well a hmmm\n";
    }
    $rec = get_rec(*STDIN);     # تمرير الـ glob بالكامل
    $rec = get_rec(*STDIN{IO}); # تمرير مقابض الملفات والأدلة معًا
    sub get_rec {
        my $fh = shift;
        return scalar <$fh>;
    }

استخدام المراجع

هذا كل ما في الأمر بخصوص إنشاء المراجع. بحلول الآن، ربما تتوق لمعرفة كيفية استخدام المراجع لاستعادة بياناتك المفقودة منذ زمن طويل. هناك عدة طرق أساسية.

سلمية بسيطة

في أي مكان تضع فيه معرفًا (أو سلسلة من المعرفات) كجزء من اسم متغير أو دالة فرعية، يمكنك استبدال المعرف بمتغير مفرد بسيط يحتوي على مرجع من النوع الصحيح:

    $bar = $$scalarref;
    push(@$arrayref, $filename);
    $$arrayref[0] = "January";
    $$hashref{"KEY"} = "VALUE";
    &$coderef(1,2,3);
    print $globref "output\n";

من المهم فهم أننا لا نقوم بفك مراجع $arrayref[0] أو $hashref{"KEY"} هناك تحديدًا. يحدث فك مراجع المتغير المفرد قبل القيام بأي عمليات بحث عن المفاتيح. وأي شيء أكثر تعقيدًا من متغير مفرد بسيط يجب أن يستخدم الطريقة 2 أو 3 أدناه. ومع ذلك، فإن "المتغير المفرد البسيط" يشمل المعرف الذي يستخدم الطريقة 1 بشكل تكراري. لذلك، فإن ما يلي يطبع "howdy".

    $refrefref = \\\"howdy";
    print $$$$refrefref;

كتلة

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

    $bar = ${$scalarref};
    push(@{$arrayref}, $filename);
    ${$arrayref}[0] = "January";
    ${$hashref}{"KEY"} = "VALUE";
    &{$coderef}(1,2,3);
    $globref->print("output\n");  # فقط إذا حُمّلت IO::Handle

باعتراف الجميع، من السخف قليلاً استخدام الأقواس المتعرجة في هذه الحالة، ولكن يمكن أن تحتوي الكتلة (BLOCK) على أي تعبير عشوائي، لا سيما التعبيرات المفهرسة:

    &{ $dispatch{$index} }(1,2,3);      # استدعِ الروتين الصحيح

بسبب القدرة على حذف الأقواس المتعرجة في الحالة البسيطة لـ $$x، غالبًا ما يخطئ الناس في اعتبار رموز فك المراجع معاملات حقيقية، ويتساءلون عن أسبقيتها. لو كانت كذلك، لكان بإمكانك استخدام الأقواس العادية بدلاً من الأقواس المتعرجة. الأمر ليس كذلك. تأمل الفرق أدناه؛ الحالة 0 هي نسخة مختصرة من الحالة 1، وليس الحالة 2:

    $$hashref{"KEY"}   = "VALUE";       # الحالة 0
    ${$hashref}{"KEY"} = "VALUE";       # الحالة 1
    ${$hashref{"KEY"}} = "VALUE";       # الحالة 2
    ${$hashref->{"KEY"}} = "VALUE";     # الحالة 3

الحالة 2 مخادعة أيضًا من حيث أنك تصل إلى متغير يسمى %hashref، وليس فك المراجع عبر $hashref إلى المجموعة المترابطة التي يفترض أنه يشير إليها. سيكون ذلك هو الحالة 3.

تدوين السهم

تحدث استدعاءات الروتين الفرعي والبحث عن عناصر المصفوفة الفردية بشكل متكرر لدرجة أنه يصبح من المرهق استخدام الطريقة 2. كشكل من أشكال التجميل النحوي، يمكن كتابة أمثلة الطريقة 2 كما يلي:

    $arrayref->[0] = "January";   # عنصر مصفوفة
    $hashref->{"KEY"} = "VALUE";  # عنصر هش
    $coderef->(1,2,3);            # استدعاء روتين فرعي

يمكن أن يكون الجانب الأيسر من السهم أي تعبير يعيد مرجعًا، بما في ذلك عملية فك مرجعية سابقة. لاحظ أن $array[$x] ليس نفس الشيء مثل "$array->[$x]" هنا:

    $array[$x]->{"foo"}->[0] = "January";

هذه إحدى الحالات التي ذكرناها سابقًا حيث يمكن للمراجع أن تنبثق إلى الوجود عندما تكون في سياق القيمة اليسارية (lvalue). قبل هذه العبارة، قد يكون $array[$x] غير معرف. إذا كان الأمر كذلك، فإنه يُعرف آليًا بمرجع هش حتى نتمكن من البحث عن "{"foo"}" بداخله. وبالمثل، سيُعرف "$array[$x]->{"foo"}" آليًا بمرجع مصفوفة حتى نتمكن من البحث عن "[0]" بداخله. تسمى هذه العملية التنشيط التلقائي (autovivification).

شيء واحد آخر هنا. السهم اختياري بين المذيلات المربعة، لذا يمكنك تقليص ما سبق إلى

    $array[$x]{"foo"}[0] = "January";

والذي يمنحك، في الحالة البسيطة لاستخدام المصفوفات العادية فقط، مصفوفات متعددة الأبعاد تمامًا مثل لغة C:

    $score[$x][$y][$z] += 42;

حسنًا، ليس تمامًا مثل مصفوفات C في الواقع. لا تعرف لغة C كيفية تنمية مصفوفاتها عند الطلب. أما Perl فيعرف.

الكائنات

إذا صادف أن يكون المرجع مرجعًا لكائن، فمن المحتمل أن تكون هناك طرق للوصول إلى الأشياء المشار إليها، ويجب عليك على الأرجح الالتزام بتلك الطرق ما لم تكن في حزمة الفئة (class package) التي تعرف طرق الكائن. بعبارة أخرى، كن لطيفًا ولا تنتهك تغليف الكائن دون سبب وجيه للغاية. لا يفرض Perl التغليف. نحن لسنا استبداديين هنا. لكننا نتوقع بعض الكياسة الأساسية.

استخدامات متنوعة

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

    if ($ref1 == $ref2) {  # مقارنة عددية بسيطة للمراجع
        print "المرجعان 1 و 2 يشيران إلى نفس الشيء\n";
    }

ينتج عن استخدام المرجع كسلسلة نصية كل من نوع المرجع إليه، بما في ذلك أي مباركة حزمة (package blessing) كما هو موصوف في perlobj، بالإضافة إلى العنوان العددي المعبر عنه بالقيم الست عشرية. يعيد معامل ref() فقط نوع الشيء الذي يشير إليه المرجع، بدون العنوان. انظر "ref" في perlfunc لمزيد من التفاصيل وأمثلة على استخدامه.

يمكن استخدام معامل bless() لربط الكائن الذي يشير إليه المرجع بحزمة تعمل كفئة (class) كائن. انظر perlobj.

يمكن فك مرجعية typeglob بنفس الطريقة التي يفك بها المرجع، لأن بنية فك المرجعية تشير دائمًا إلى نوع المرجع المطلوب. لذا فإن "${*foo}" و "${\$foo}" كلاهما يشيران إلى نفس المتغير السلمي (scalar).

إليك حيلة لاستكمال استدعاء دالة فرعية داخل سلسلة نصية:

    print "أعاد الروتين الفرعي @{[mysub(1,2,3)]} تلك المرة.\n";

طريقة عملها هي أنه عندما تظهر "@{...}" في السلسلة المقتبسة اقتباسًا مزدوجًا، فإنها تُقيم ككتلة. تنشئ الكتلة مرجعًا لمصفوفة مجهولة تحتوي على نتائج الاستدعاء لـ "mysub(1,2,3)". لذا فإن الكتلة بأكملها تعيد مرجعًا لمصفوفة، والتي تُفك مراجعها بعد ذلك بواسطة "@{...}" وتُدرج في السلسلة المقتبسة مزدوجًا. هذه الحيلة مفيدة أيضًا للتعبيرات العشوائية:

    print "ينتج عن ذلك @{[$n + 5]} من الأدوات\n";

وبالمثل، يمكن فك مرجعية تعبير يعيد مرجعًا إلى متغير سلمي (scalar) عبر "${...}". وبالتالي، يمكن كتابة التعبير أعلاه كما يلي:

    print "ينتج عن ذلك ${\($n + 5)} من الأدوات\n";

مراجع دائرية

من الممكن إنشاء "مرجع دائري" في Perl، مما قد يؤدي إلى تسرب في الذاكرة. يحدث المرجع الدائري عندما يحتوي مرجعان على مرجع لبعضهما البعض، على هذا النحو:

    my $foo = {};
    my $bar = { foo => $foo };
    $foo->{bar} = $bar;

يمكنك أيضًا إنشاء مرجع دائري باستخدام متغير واحد:

    my $foo;
    $foo = \$foo;

في هذه الحالة، لن يصل عدد المراجع للمتغيرات أبدًا إلى 0، ولن تُجمع المراجع أبدًا بواسطة جامع القمامة (garbage collector). يمكن أن يؤدي هذا إلى تسرب في الذاكرة.

نظرًا لأن الكائنات في Perl تُنفذ كمراجع، فمن الممكن وجود مراجع دائرية مع الكائنات أيضًا. تخيل فئة TreeNode حيث يشير كل عقدة إلى عقدها الأب والأبناء. أي عقدة لها أب ستكون جزءًا من مرجع دائري.

يمكنك كسر المراجع الدائرية عن طريق إنشاء "مرجع ضعيف". المرجع الضعيف لا يزيد عدد المراجع للمتغير، مما يعني أن الكائن يمكن أن يخرج عن النطاق ويتم تدميره. يمكنك إضعاف مرجع باستخدام وظيفة "weaken" المصدرة بواسطة وحدة Scalar::Util، أو المتوفرة كـ "builtin::weaken" مباشرة في إصدار Perl 5.35.7 أو أحدث.

إليك كيف يمكننا جعل المثال الأول أكثر أمانًا:

    use Scalar::Util 'weaken';
    my $foo = {};
    my $bar = { foo => $foo };
    $foo->{bar} = $bar;
    weaken $foo->{bar};

أُضعف المرجع من $foo إلى $bar. عندما يخرج المتغير $bar عن النطاق، سيُجمع بواسطة جامع القمامة. في المرة القادمة التي تنظر فيها إلى قيمة مفتاح "$foo->{bar}"، ستكون "undef".

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

مراجع رمزية

قلنا إن المراجع تظهر للوجود حسب الضرورة إذا كانت غير محددة، لكننا لم نقل ماذا يحدث إذا كانت القيمة المستخدمة كمرجع محددة بالفعل، ولكنها ليست مرجعًا حقيقيًا (hard reference). إذا استخدمتها كمرجع، فستُعامل كمرجع رمزي. أي أن قيمة المتغير المفرد تُعتبر اسم المتغير، بدلاً من كونها رابطًا مباشرًا لقيمة (مجهولة المصدر ربما).

كثيرًا ما يتوقع الناس أن تعمل بهذه الطريقة. وهي كذلك بالفعل.

    $name = "foo";
    $$name = 1;                 # يضبط $foo
    ${$name} = 2;               # يضبط $foo
    ${$name x 2} = 3;           # يضبط $foofoo
    $name->[0] = 4;             # يضبط $foo[0]
    @$name = ();                # يمسح @foo
    &$name();                   # يستدعي &foo()
    $pack = "THAT";
    ${"${pack}::$name"} = 5;    # يضبط $THAT::foo بدون eval

هذا أمر قوي، وخطير قليلاً، من حيث أنه من الممكن أن تنوي (بأقصى درجات الإخلاص) استخدام مرجع حقيقي، وتستخدم عن طريق الخطأ مرجعًا رمزيًا بدلاً منه. للحماية من ذلك، يمكنك قول

    use strict 'refs';

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

    no strict 'refs';

متغيرات الحزمة فقط (المتغيرات العامة، حتى لو كانت محلية) هي المرئية للمراجع الرمزية. المتغيرات المعجمية (المصرح عنها باستخدام my()) ليست في جدول الرموز، وبالتالي فهي غير مرئية لهذه الآلية. فعلى سبيل المثال:

    local $value = 10;
    $ref = "value";
    {
        my $value = 20;
        print $$ref;
    }

سيظل هذا يطبع 10، وليس 20. تذكر أن local() تؤثر على متغيرات الحزمة، والتي تكون جميعها "عالمية" بالنسبة للحزمة.

مراجع ليست رمزية تمامًا

يمكن للأقواس حول المرجع الرمزي أن تعمل ببساطة على عزل معرف أو اسم متغير عن بقية التعبير، تمامًا كما كانت تفعل دائمًا داخل السلسلة النصية. على سبيل المثال،

    $push = "pop on ";
    print "${push}over";

كانت تعني دائمًا طباعة "pop on over"، على الرغم من أن push كلمة محجوزة. تم تعميم هذا ليعمل بنفس الطريقة بدون علامات الاقتباس المزدوجة المحيطة، بحيث أن

    print ${push} . "over";

وحتى

    print ${ push } . "over";

سيكون له نفس التأثير. هذا البناء لا يعتبر مرجعًا رمزيًا عندما تستخدم strict refs:

    use strict 'refs';
    ${ bareword };      # حسنًا، تعني $bareword.
    ${ "bareword" };    # خطأ، مرجع رمزي.

وبالمثل، نظرًا لكل عمليات التذييل (subscripting) التي تتم باستخدام كلمات مفردة، تنطبق نفس القاعدة على أي كلمة مجردة (bareword) تُستخدم لتذييل الهش. لذا الآن، بدلاً من كتابة

    $hash{ "aaa" }{ "bbb" }{ "ccc" }

يمكنك كتابة فقط

    $hash{ aaa }{ bbb }{ ccc }

ولا تقلق بشأن ما إذا كانت المذيلات كلمات محجوزة. في الحالة النادرة التي ترغب فيها فعلاً في القيام بشيء مثل

    $hash{ shift }

يمكنك فرض التفسير ككلمة محجوزة بإضافة أي شيء يجعلها أكثر من كلمة مجردة:

    $hash{ shift() }
    $hash{ +shift }
    $hash{ shift @_ }

سيقوم pragma الاستخدام "use warnings" أو مفتاح -w بتحذيرك إذا فسر كلمة محجوزة كسلسلة نصية. لكنه لن يحذرك بعد الآن بشأن استخدام كلمات صغيرة، لأن السلسلة تكون مقتبسة فعليًا.

أشباه الهش: استخدام مصفوفة كهش

أُزيلت التجزئات الوهمية (Pseudo-hashes) من بيرل (Perl). وما تزال الميزة الإرشادية (pragma) للحقول متاحة.

قوالب الدوال

كما شُرح أعلاه، تُنشئ الدالة المجهولة التي تملك صلاحية الوصول إلى المتغيرات المعجمية المرئية وقت تصريف تلك الدالة ما يُسمى «غلقة» (closure). حيث تحتفظ بصلاحية الوصول إلى تلك المتغيرات حتى وإن لم تُنفذ إلا لاحقًا، كما هو الحال في معالج إشارات أو استدعاء Tk راجع.

يسمح لنا استخدام الإغلاق كقالب دالة بتوليد العديد من الدوال التي تعمل بشكل متشابه. لنفترض أنك أردت دوالاً مسماة بأسماء الألوان تقوم بتوليد تغييرات خطوط HTML لمختلف الألوان:

    print "Be ", red("careful"), "with that ", green("light");

ستكون الدالتان red() و green() مماثلتين. ولإنشائهما، سنخصص «غلقة» إلى typeglob يحمل اسم الدالة التي نحاول بناءها.

    @colors = qw(red blue green yellow orange purple violet);
    for my $name (@colors) {
        no strict 'refs';       # السماح بالتلاعب بجدول الرموز
        *$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" };
    }

الآن تبدو كل تلك الدوال المختلفة وكأنها موجودة بشكل مستقل. يمكنك استدعاء red() و RED() و blue() و BLUE() و green()، وما إلى ذلك. توفر هذه التقنية في كل من وقت التصريف واستهلاك الذاكرة، وهي أقل عرضة للأخطاء أيضًا، نظرًا لأن فحص الصياغة يحدث وقت التصريف. ومن الضروري أن تكون أي متغيرات في الروتين الفرعي المجهول معجمية من أجل إنشاء «غلقة» سليمة. وهذا هو السبب وراء استخدام "my" في متغير تكرار الحلقة.

هذا أحد الأماكن القليلة التي يكون فيها إعطاء نموذج مبدئي (prototype) للإغلاق منطقيًا. إذا أردت فرض سياق سلمي (scalar context) على وسائط هذه الدوال (ربما ليست فكرة حكيمة لهذا المثال بالذات)، كان بإمكانك كتابتها بهذه الطريقة بدلاً من ذلك:

    *$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };

ومع ذلك، بما أن فحص النموذج المبدئي يتم في وقت التجميع، فإن التعيين أعلاه يحدث بعد فوات الأوان ليكون ذا فائدة كبيرة. يمكنك معالجة ذلك بوضع حلقة التعيينات بأكملها داخل كتلة BEGIN، مما يجبرها على الحدوث أثناء التجميع.

الوصول إلى المتغيرات اللفظية التي تتغير بمرور الوقت - مثل تلك الموجودة في حلقة "for" أعلاه، وهي أساسًا أسماء مستعارة لعناصر من النطاقات اللفظية المحيطة - لا يعمل إلا مع الروتينات الفرعية المجهولة، وليس مع الروتينات الفرعية المسماة. بشكل عام، لا تتداخل الروتينات الفرعية المسماة بشكل صحيح ويجب التصريح عنها فقط في نطاق الحزمة الرئيس.

هذا لأن الروتينات الفرعية المسماة تُنشأ في وقت التجميع، لذا يتم تعيين متغيراتها اللفظية إلى المتغيرات اللفظية للأب من أول تنفيذ لكتلة الأب. إذا دُخل نطاق الأب مرة ثانية، تُنشأ متغيراته اللفظية مرة أخرى، بينما تظل الروتينات الفرعية المتداخلة تشير إلى القديمة.

تتمكن الروتينات الفرعية المجهولة من الالتقاط في كل مرة تُنفذ فيها عامل "sub"، حيث تُنشأ آليًا. إذا كنت معتادًا على استخدام الروتينات الفرعية المتداخلة في لغات برمجة أخرى بمتغيراتها الخاصة، فسيتعين عليك بذل جهد إضافي في بيرل. إن الكتابة البديهية لهذا النوع من الأشياء تتسبب في تحذيرات غامضة حول «لن تظل مشتركة» (will not stay shared) للأسباب الموضحة أعلاه. على سبيل المثال، هذا لن يعمل:

    sub outer {
        my $x = $_[0] + 35;
        sub inner { return $x * 19 }   # خطأ
        return $x + inner();
    }

الحل البديل هو ما يلي:

    sub outer {
        my $x = $_[0] + 35;
        local *inner = sub { return $x * 19 };
        return $x + inner();
    }

الآن لا يمكن استدعاء inner() إلا من داخل outer()، بسبب التعيينات المؤقتة للروتين الفرعي المجهول. ولكن عندما يحدث ذلك، يكون له حق الوصول الطبيعي إلى المتغير اللفظي $x من نطاق outer() في الوقت الذي يتم فيه استدعاء outer.

لهذا تأثير مثير للاهتمام يتمثل في إنشاء دالة محلية لدالة أخرى، وهو أمر لا يدعمه Perl عادةً.

صيغة فك المراجع اللاحقة (Postfix)

بدءًا من الإصدار v5.20.0، أصبحت صيغة اللاحقة (postfix) لاستخدام المراجع متاحة. وهي تعمل كما هو موصوف في "استخدام المراجع"، ولكن بدلاً من استخدام رمز (sigil) سابِق، يُستخدم رمز ونجمة لاحقين.

على سبيل المثال:

    $r = \@a;
    @b = $r->@*; # مكافئ لـ @$r أو @{ $r }
    $r = [ 1, [ 2, 3 ], 4 ];
    $r->[1]->@*;  # مكافئ لـ @{ $r->[1] }
    $aref->@* = (1, 2, 3);  # مماثل لـ @{ $aref } = (1, 2, 3)

في إصداري Perl 5.20 و 5.22، يجب تفعيل هذه الصيغة باستخدام use feature 'postderef'. وبدءًا من Perl 5.24، لا يلزم التصريح عن أي ميزة لجعلها متاحة.

ينبغي أن يعمل فك المراجع اللاحق في جميع الظروف التي يعمل فيها فك المراجع الكتلي (المحيط)، ويجب أن يكون مكافئًا له تمامًا. تسمح هذه الصيغة بكتابة وقراءة فك المراجع بالكامل من اليسار إلى اليمين. عُرّفت المكافئات التالية:

  $sref->$*;  # يماثل  ${ $sref }
  $aref->@*;  # يماثل  @{ $aref }
  $aref->$#*; # يماثل $#{ $aref }
  $href->%*;  # يماثل  %{ $href }
  $cref->&*;  # يماثل  &{ $cref }
  $gref->**;  # يماثل  *{ $gref }

لاحظ بشكل خاص أن "$cref->&*" ليس مكافئًا لـ $cref->()، ويمكن أن يخدم أغراضًا مختلفة.

يمكن استخراج عناصر النوع العام (Glob) عبر ميزة فك المراجع اللاحقة:

  $gref->*{SCALAR}; # يماثل *{ $gref }{SCALAR}

يمكن استخدام فك مراجع المصفوفات والمتغيرات اللاحقة في استكمال السلاسل النصية (علامات الاقتباس المزدوجة أو معامل "qq")، ولكن فقط في حال تفعيل ميزة "postderef_qq". كما يُدعم استكمال الوصول إلى أعلى فهرس للمصفوفة اللاحقة ("->$#*") عند تفعيل ميزة "postderef_qq".

تشريح المراجع اللاحقة

يمكن أيضًا أخذ شرائح القيم للمصفوفات والمجموعات المترابطة (hashes) باستخدام تدوين فك المراجع اللاحق، مع المكافئات التالية:

  $aref->@[ ... ];  # يماثل @$aref[ ... ]
  $href->@{ ... };  # يماثل @$href{ ... }

تشريح أزواج المفاتيح/القيم اللاحق، الذي أُضيف في 5.20.0 ووُثق في قسم Key/Value Hash Slices من perldata، يعمل أيضًا كما هو متوقع:

  $aref->%[ ... ];  # يماثل %$aref[ ... ]
  $href->%{ ... };  # يماثل %$href{ ... }

كما هو الحال مع المصفوفة اللاحقة، يمكن استخدام فك مراجع تشريح القيم اللاحق في استكمال السلاسل النصية (علامات الاقتباس المزدوجة أو معامل "qq")، ولكن فقط في حال تفعيل ميزة "postderef_qq".

الإسناد إلى المراجع

بدءًا من v5.22.0، يمكن الإسناد إلى معامل المراجعة. وهو يؤدي عملية استعارة (aliasing)، بحيث يصبح اسم المتغير المشار إليه في الجانب الأيسر مستعارًا للشيء المشار إليه في الجانب الأيمن:

    \$x = \$y; # يشير $x و $y الآن إلى نفس المتغير
    \&foo = \&bar; # تعني foo() الآن bar()

يجب تفعيل هذه الصيغة باستخدام "use feature 'refaliasing'". وهي تجريبية، وستُصدر تحذيرًا بشكل مبدئي ما لم يُفعّل no warnings 'experimental::refaliasing'.

يمكن الإسناد إلى هذه الأشكال، وتؤدي إلى تقييم الجانب الأيمن في سياق مفرد (scalar):

    \$scalar
    \@array
    \%hash
    \&sub
    \my $scalar
    \my @array
    \my %hash
    \state $scalar # أو @array، إلخ.
    \our $scalar   # إلخ.
    \local $scalar # إلخ.
    \local our $scalar # إلخ.
    \$some_array[$index]
    \$some_hash{$key}
    \local $some_array[$index]
    \local $some_hash{$key}
    condition ? \$this : \$that[0] # إلخ.

تؤدي عمليات التشريح والأقواس إلى تقييم الجانب الأيمن في سياق قائمة (list):

    \@array[5..7]
    (\@array[5..7])
    \(@array[5..7])
    \@hash{'foo','bar'}
    (\@hash{'foo','bar'})
    \(@hash{'foo','bar'})
    (\$scalar)
    \($scalar)
    \(my $scalar)
    \my($scalar)
    (\@array)
    (\%hash)
    (\&sub)
    \(&sub)
    \($foo, @bar, %baz)
    (\$foo, \@bar, \%baz)

يجب أن يكون كل عنصر في الجانب الأيمن مرجعًا لبيانات من النوع الصحيح. الأقواس التي تحيط بمصفوفة مباشرة (وربما أيضًا "my"/"state"/"our"/"local") ستجعل كل عنصر في المصفوفة مستعارًا للمتغير المقابل المشار إليه في الجانب الأيمن:

    \(@x) = \(@y); # أصبح لـ @x و @y نفس العناصر الآن
    \my(@x) = \(@y); # وبالمثل
    \(my @x) = \(@y); # وبالمثل
    push @x, 3; # ولكن الآن @x بها عنصر إضافي تفتقر إليه @y
    \(@x) = (\$x, \$y, \$z); # تحتوي @x الآن على $x و $y و $z

يُحظر دمج هذا الشكل مع "local" ووضع أقواس مباشرة حول المجموعات المترابطة (hashes) (لأنه من غير الواضح ما الذي ينبغي أن تفعله):

    \local(@array) = foo(); # خطأ
    \(%hash)       = bar(); # خطأ

يمكن دمج الإسناد إلى المراجع وغير المراجع في القوائم والتعبيرات الثلاثية الشرطية، طالما أن القيم في الجانب الأيمن هي من النوع الصحيح لكل عنصر في اليسار، وإن كان هذا قد يجعل الكود غامضًا:

    (my $tom, \my $dick, \my @harry) = (\1, \2, [1..3]);
    # $tom هو الآن \1
    # $dick هو الآن 2 (للقراءة فقط)
    # @harry هي (1,2,3)
    my $type = ref $thingy;
    ($type ? $type eq 'ARRAY' ? \@foo : \$bar : $baz) = $thingy;

إسناد المراجع في سياق قائمة يعيد قائمة من المراجع لكل قيمة في الجانب الأيسر. فعلى سبيل المثال

    @b = ((\$l1, \$l2, \(@a)) = (\$r1, \$r2, $\r3, \$4));

يكافئ

    (\$l1, \$l2, \(@a)) = (\$r1, \$r2, $\r3, \$4);
    @b = (\$l1, \$l2, \$a[0], \$a[1]);

يمكن لحلقة "foreach" أيضًا أن تأخذ منشئ مرجع لمتغير الحلقة الخاص بها، وإن كانت الصيغة تقتصر على أحد ما يلي، مع إمكانية استخدام "my" أو "state" أو "our" بعد الخط المائل العكسي:

    \$s
    \@a
    \%h
    \&c

لا يُسمح باستخدام الأقواس. هذه الميزة مفيدة بشكل خاص لمصفوفات المصفوفات، أو مصفوفات المجموعات المترابطة (arrays-of-hashes):

    foreach \my @a (@array_of_arrays) {
        frobnicate($a[0], $a[-1]);
    }
    foreach \my %h (@array_of_hashes) {
        $h{gelastic}++ if $h{type} eq 'funny';
    }

تنبيه: الاستعارة (Aliasing) لا تعمل بشكل صحيح مع الإغلاقات (closures). إذا حاولت استعارة متغيرات معجمية من دالة فرعية داخلية أو "eval"، فسيكون الاستعارة مرئيًا فقط داخل تلك الدالة الفرعية، ولن يؤثر على الدالة الخارجية التي صُرّح عن المتغيرات فيها. هذا السلوك الغريب عرضة للتغيير.

التصريح عن مرجع لمتغير

بدءًا من v5.26.0، يمكن أن يأتي معامل المراجعة بعد "my"، أو "state"، أو "our"، أو "local". يجب تفعيل هذه الصيغة باستخدام "use feature 'declared_refs'". وهي تجريبية، وستُصدر تحذيرًا بشكل مبدئي ما لم يُفعّل "no warnings 'experimental::refaliasing'".

هذه الميزة تجعل هذه:

    my \$x;
    our \$y;

تكافئ:

    \my $x;
    \our $x;

الغرض منها رئيس هو الاستخدام في الإسنادات إلى المراجع (انظر "الإسناد إلى المراجع" أعلاه). كما أنها تسمح باستخدام الخط المائل العكسي على بعض العناصر فقط في قائمة المتغيرات المُصرح عنها:

    my ($foo, \@bar, \%baz); # يكافئ:  my $foo, \my(@bar, %baz);

تحذير: لا تستخدم المراجع كمفاتيح للتجزئة

لا يمكنك (بشكل مفيد) استخدام مرجع كمفتاح لمجموعة مترابطة. سيتم تحويله إلى سلسلة نصية:

    $x{ \$x } = $x;

إذا حاولت إلغاء مرجعية المفتاح، فلن يُنفذ إلغاء مرجعية حقيقي، ولن تحقق ما تحاول القيام به. قد ترغب في القيام بشيء أقرب إلى

    $r = \@a;
    $x{ $r } = $r;

وعندها على الأقل يمكنك استخدام values()، والتي ستكون مراجع حقيقية، بدلاً من keys()، التي لن تكون كذلك.

توفر وحدة Tie::RefHash القياسية حلاً مناسبًا لهذا الأمر.

انظر أيضًا

بصرف النظر عن الوثائق البديهية، يمكن أن يكون الكود المصدري تعليميًا. يمكن العثور على بعض الأمثلة المعقدة لاستخدام المراجع في اختبار التراجع t/op/ref.t في دليل مصدر Perl.

انظر أيضًا perldsc و perllol لمعرفة كيفية استخدام المراجع لإنشاء هياكل بيانات معقدة، و perlootut و perlobj لمعرفة كيفية استخدامها لإنشاء كائنات.

ترجمة

تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.

11 يوليو 2025 perl v5.42.0