我有許多更高級的實用程序函數,它們接受代碼引用並將該代碼應用於某些數據。其中一些功能需要在執行子程序期間對變量進行本地化。在開始的時候,我用caller
來確定要定位哪個包入,以類似的方式,如本示例中所示reduce
功能:但是,一旦在Perl中,確定coderef包的最可靠方法是什麼?
sub reduce (&@) {
my $code = shift;
my $caller = caller;
my ($ca, $cb) = do {
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
local (*a, *b) = local (*$ca, *$cb);
$a = shift;
while (@_) {
$b = shift;
$a = $code->()
}
$a
}
最初,此技術工作得很好,因爲我試圖寫的包裝函數圍繞高階函數,找出正確的調用者變得複雜。
sub reduce_ref (&$) {&reduce($_[0], @{$_[1]})}
現在爲了reduce
工作,我需要這樣的:
my ($ca, $cb) = do {
my $caller = 0;
$caller++ while caller($caller) =~ /^This::Package/;
no strict 'refs';
map \*{caller($caller).'::'.$_} => qw(a b)
};
在這一點上它成爲了一個問題,其中包跳過與從不使用該函數的紀律相結合從這些包中。必須有更好的方法。
事實證明,高階函數作爲參數的子例程包含足夠的元數據來解決問題。我當前的解決方案是使用B
自檢模塊來確定傳入子例程的編譯隱藏。這樣,無論在編譯代碼和執行代碼之間發生了什麼,高階函數總是知道要本地化的正確包。
my ($ca, $cb) = do {
require B;
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
所以我最終的問題是,如果這是在這種情況下確定來電者套餐的最佳方式?有沒有其他的方式,我沒有想到?當前的解決方案是否有一些等待發生的錯誤?
這似乎是非常依賴於實現的依賴性......你對於在未來的Perl版本中沒有改變這一點有多自信?使用對象代替原始函數會不會更簡單,更健壯?是否讓每個對象都存儲一個函數並記住相應的包? – Nemo