2015-01-21 24 views
5

我想在我的一個面向對象的包中提供一個排序函數,它接受一個塊並使$ a和$ b像標準的Perl sort一樣可用。在一個對象中包裝perl的排序功能

首先,我想要在包含該包裹的排序功能包做一個簡化版本:

# In package My::Object 
sub sort { 
    my $self = shift; 
    my $block = \&{shift @_}; 

    return sort $block @{$self->{arrayRef}}; # I want to use the passed in block with array data contained in this object 
} 

然後在客戶端的傳遞,它定義了比較器來運行塊爲例用於排序:

my $obj = My::Object->new([3, 1, 5, 6, 2, 4]); # As an example, these values will be come arrayRef from above 
my @sortedVals = $obj->sort({ $a < $b }); 

有沒有辦法做我想要做的,同時仍然能夠使用Perl的sort

回答

8

大部分。

要使用裸塊作爲子例程語法,您需要使用&prototype。一般而言,您應該避免使用原型,但將子例程作爲裸塊傳遞是可接受的少數幾次之一。不幸的是,因爲它們必須在編譯時確定並應用,所以原型不適用於方法。所以你必須使用完整的匿名子程序語法,sub { ... }

my @sortedVals = $obj->sort(sub { $a <=> $b }); 

$a$b是那種子程序在聲明的封裝中的全局變量(讓我們說這是Some::Caller)。在班級內部運行時,排序將設置爲$My::Object::a$My::Object::b,但子程序將查找$Some::Caller::a$Some::Caller::b。您可以通過將它們的$a$b別名爲$a$b來解決此問題。

sub sort { 
    my $self = shift; 
    my $block = shift; 

    no strict 'refs'; 
    local *{caller.'::a'} = *a; 
    local *{caller.'::b'} = *b; 

    return sort $block @{$self->{arrayRef}}; 
} 

local使得全球的臨時副本塊的持續時間,這包括其它子程序調用,因此這將影響排序。然後當方法完成時,該值將恢復到原來的值。

Perl全局變量存儲在typeglobs中,其中包含所有具有相同名稱的全局變量。包含$a@a%a。通過複製呼叫者的typeglob,當$My::Object::a發生變化時,呼叫者的$a也會發生變化。他們是別名。

*{...}語法可讓您使用其他變量或表達式按名稱獲取全局變量。這被稱爲symbolic reference。現在我們可以看到來電者的*a。使用這種語法通常是一個錯誤,所以你必須關掉strict否則Perl不會讓你這樣做。

+2

欣賞您在SO上的帖子。 – 2015-01-21 06:34:46

+0

'* {...}語法通過名稱引用全局變量。「這似乎是描述該語法的一種有趣的方式 - 特別是在解釋* typeglob *之後。該語法是另一種編寫* typeglob *的方法,其中* typeglob *表示具有指定名稱的所有全局變量..... – 7stud 2015-01-21 09:20:08

+0

指定所有當前程序包的全局「a」變量,* typeglob * * *對於所有調用者的全局'a'變量,* typeglob *'* {caller。':: a'}將導致當前包的全局'a'變量成爲調用包的全局'a'變量的別名。這意味着當你改變當前包中的全局'a'變量時,它會改變調用者包中的全局'a'變量。* – 7stud 2015-01-21 09:21:33