سؤال

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

مكن رمز مثل:مجموعة fp [فتح "x"] تأجيل("وثيقة $fp");

التي استشهد عند النطاق الحالي خرجت.الفائدة الرئيسية هو أنه دائما الاحتجاج لا يهم كيف/أين أترك المجال.

حتى لا ينفذ شيئا من هذا القبيل في بيرل ولكن يبدو أن هناك طريقة أسهل.التعليقات والانتقادات موضع ترحيب.

الطريقة التي فعلت ذلك في بيرل:

  • إنشاء العالمية ، وتعادل المتغير الذي يحمل مجموعة من الغواصات ليتم تنفيذها.
  • كلما كنت تريد جدولة fn أن يحتج بها على خروج الاستخدام المحلي إلى تغيير مجموعة.عندما أغادر النطاق الحالي, Perl التغيرات العالمية إلى القيمة السابقة لأن العالمية يرتبط أعلم عند هذه القيمة تغيير يحدث و يمكن الاحتجاج الغواصات في القائمة.

رمز الفعلي هو أدناه.

هل هناك طريقة أفضل للقيام بذلك ؟ يبدو أن هذا سيكون المطلوبة عادة القدرة.

use strict;

package tiescalar;

sub TIESCALAR {
    my $class = shift;

    my $self = {};
    bless $self, $class;
    return $self;
}

sub FETCH {
    my $self = shift;
    return $self->{VAL};
}

sub STORE {
    my $self = shift;
    my $value = shift;

    if (defined($self->{VAL}) && defined($value)) {
    foreach my $s (@{$self->{VAL}}) { &$s; }
    }
    $self->{VAL} = $value;
}

1;

package main;

our $h;
tie($h, 'tiescalar');
$h = [];
printf "1\n";
printf "2\n";

sub main { 
    printf "3\n";
    local $h = [sub{printf "9\n"}];
    push(@$h, sub {printf "10\n";});
    printf "4\n";
    { 
    local $h = [sub {printf "8\n"; }];
    mysub();
    printf "7\n";
    return;
    }
}

sub mysub {
    local $h = [sub {printf "6\n"; }];
    print "5\n";
}

main();

printf "11\n";
هل كانت مفيدة؟

المحلول

إن حالة محددة بالفعل التعامل معها إذا كنت تستخدم المعجمية filehandles (بدلا من الأسلوب القديم bareword filehandles).في الحالات الأخرى ، يمكنك دائما استخدام تدمير طريقة كائن مضمونة للذهاب إلى الصفر المراجع عندما يخرج من نطاق:

#!/usr/bin/perl

use strict;
use warnings;

for my $i (1 .. 5) {
    my $defer = Defer::Sub->new(sub { print "end\n" });
    print "start\n$i\n";
}

package Defer::Sub;

use Carp;

sub new {
    my $class = shift;
    croak "$class requires a function to call\n" unless @_;
    my $self  = {
        func => shift,
    };
    return bless $self, $class;
}

sub DESTROY { 
    my $self = shift;
    $self->{func}();
}

ETA:أنا مثل براين اسم أفضل نطاق::OnExit هو أكثر اسم وصفي.

نصائح أخرى

بدلا من استخدام التعادل هذا, أعتقد أنني كنت مجرد إنشاء كائن.يمكنك أيضا تجنب local بهذه الطريقة أيضا.

{
my $defer = Scope::OnExit->new( @subs );
$defer->push( $other_sub ); # and pop, shift, etc

...
}

عندما متغير يخرج من نطاق لديك فرصة أن تفعل أشياء في تدمير الأسلوب.

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

sub TIESCALAR { bless { VAL => [] }, $_[0] }

sub STORE {
    my( $self, $value )  = @_;

    carp "Can only store array references!" unless ref $value eq ref [];

    foreach { @$value } {
        carp "There should only be code refs in the array"
            unless ref $_ eq ref sub {}
        }

    foreach ( @{ $self->{VAL}} ) { $_->() }


    $self->{VAL} = $value;
    }

قد ترغب في محاولة الخروج ب::هاكات::EndOfScope

أعتقد أن هذه الأعمال:

   use B::Hooks::EndOfScope; 

   sub foo {
      on_scope_end { 
               $codehere;
      };
      $morecode
      return 1; # scope end code executes.
   }

   foo();

أعتقد أنك تريد شيئا مثل نطاق::حارس, لكنه لا يستطيع أن يدفع.هممم.

شكرا

بشكل مسلي ،

sub OnLeavingScope::DESTROY { ${$_[0]}->() }

استخدامها مثل:

{
    ...
    my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
    my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
    ...
}

(على مستوى إضافي من وجود مرجع إلى مرجع الفرعية هو ضروري فقط للتغلب الأمثل (هذا القول خطأ) عند استخدام غير إغلاق مجهول الفرعية.)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top